]> sipb.mit.edu Git - ikiwiki.git/blob - IkiWiki/Plugin/attachment.pm
8d3d60de611a59fef4037c61a5db1a303279cac9
[ikiwiki.git] / IkiWiki / Plugin / attachment.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::attachment;
3
4 use warnings;
5 use strict;
6 use IkiWiki 2.00;
7
8 sub import { #{{{
9         hook(type => "checkconfig", id => "attachment", call => \&checkconfig);
10         hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup);
11         hook(type => "formbuilder", id => "attachment", call => \&formbuilder);
12 } # }}}
13
14 sub checkconfig () { #{{{
15         $config{cgi_disable_uploads}=0;
16 } #}}}
17
18 sub formbuilder_setup (@) { #{{{
19         my %params=@_;
20         my $form=$params{form};
21
22         if ($form->field("do") eq "edit") {
23                 $form->field(name => 'attachment', type => 'file');
24         }
25         elsif ($form->title eq "preferences") {
26                 my $session=$params{session};
27                 my $user_name=$session->param("name");
28
29                 $form->field(name => "allowed_attachments", size => 50,
30                         fieldset => "admin",
31                         comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")");
32                 if (! IkiWiki::is_admin($user_name)) {
33                         $form->field(name => "allowed_attachments", type => "hidden");
34                 }
35                 if (! $form->submitted) {
36                         $form->field(name => "allowed_attachments", force => 1,
37                                 value => IkiWiki::userinfo_get($user_name, "allowed_attachments"));
38                 }
39                 if ($form->submitted && $form->submitted eq 'Save Preferences') {
40                         if (defined $form->field("allowed_attachments")) {
41                                 IkiWiki::userinfo_set($user_name, "allowed_attachments",
42                                 $form->field("allowed_attachments")) ||
43                                         error("failed to set allowed_attachments");
44                         }
45                 }
46         }
47 } #}}}
48
49 sub formbuilder (@) { #{{{
50         my %params=@_;
51         my $form=$params{form};
52
53         return if $form->field("do") ne "edit";
54
55         if ($form->submitted eq "Upload") {
56                 my $q=$params{cgi};
57                 my $filename=$q->param('attachment');
58                 if (! defined $filename || ! length $filename) {
59                         # no file, so do nothing
60                         return;
61                 }
62                 
63                 # This is an (apparently undocumented) way to get the name
64                 # of the temp file that CGI writes the upload to.
65                 my $tempfile=$q->tmpFileName($filename);
66                 
67                 # Put the attachment in a subdir of the page it's attached
68                 # to, unless that page is an "index" page.
69                 my $page=$form->field('page');
70                 $page=~s/(^|\/)index//;
71                 $filename=$page."/".IkiWiki::basename($filename);
72                 
73                 # To untaint the filename, escape any hazardous characters,
74                 # and make sure it isn't pruned.
75                 $filename=IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($filename));
76                 if (IkiWiki::file_pruned($filename, $config{srcdir})) {
77                         error(gettext("bad attachment filename"));
78                 }
79                 
80                 # Check that the user is allowed to edit a page with the
81                 # name of the attachment.
82                 IkiWiki::check_canedit($filename, $q, $params{session}, 1);
83                 
84                 # Use a special pagespec to test that the attachment is valid.
85                 my $allowed=1;
86                 foreach my $admin (@{$config{adminuser}}) {
87                         my $allowed_attachments=IkiWiki::userinfo_get($admin, "allowed_attachments");
88                         if (defined $allowed_attachments &&
89                             length $allowed_attachments) {
90                                 $allowed=pagespec_match($filename,
91                                         $allowed_attachments,
92                                         file => $tempfile);
93                                 last if $allowed;
94                         }
95                 }
96                 if (! $allowed) {
97                         error(gettext("attachment rejected")." ($allowed)");
98                 }
99
100                 # Needed for fast_file_copy.
101                 require IkiWiki::Render;
102
103                 # Move the attachment into place.
104                 # Try to use a fast rename; fall back to copying.
105                 IkiWiki::prep_writefile($filename, $config{srcdir});
106                 unlink($config{srcdir}."/".$filename);
107                 if (! rename($tempfile, $config{srcdir}."/".$filename)) {
108                         my $fh=$q->upload('attachment');
109                         if (! defined $fh || ! ref $fh) {
110                                 error("failed to get filehandle");
111                         }
112                         binmode($fh);
113                         writefile($filename, $config{srcdir}, undef, 1, sub {
114                                 IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_);
115                         });
116                 }
117
118                 # TODO add to vcs
119                 
120                 # TODO trigger a wiki build if there's no vcs
121         }
122 } # }}}
123
124 package IkiWiki::PageSpec;
125
126 sub parsesize ($) { #{{{
127         my $size=shift;
128         no warnings;
129         my $base=$size+0; # force to number
130         use warnings;
131         my $multiple=1;
132         if ($size=~/kb?$/i) {
133                 $multiple=2**10;
134         }
135         elsif ($size=~/mb?$/i) {
136                 $multiple=2**20;
137         }
138         elsif ($size=~/gb?$/i) {
139                 $multiple=2**30;
140         }
141         elsif ($size=~/tb?$/i) {
142                 $multiple=2**40;
143         }
144         return $base * $multiple;
145 } #}}}
146
147 sub match_maxsize ($$;@) { #{{{
148         shift;
149         my $maxsize=eval{parsesize(shift)};
150         if ($@) {
151                 return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)");
152         }
153
154         my %params=@_;
155         if (! exists $params{file}) {
156                 return IkiWiki::FailReason->new("no file specified");
157         }
158
159         if (-s $params{file} > $maxsize) {
160                 return IkiWiki::FailReason->new("file too large");
161         }
162         else {
163                 return IkiWiki::SuccessReason->new("file not too large");
164         }
165 } #}}}
166
167 sub match_minsize ($$;@) { #{{{
168         shift;
169         my $minsize=eval{parsesize(shift)};
170         if ($@) {
171                 return IkiWiki::FailReason->new("unable to parse minsize (or number too large)");
172         }
173
174         my %params=@_;
175         if (! exists $params{file}) {
176                 return IkiWiki::FailReason->new("no file specified");
177         }
178
179         if (-s $params{file} < $minsize) {
180                 return IkiWiki::FailReason->new("file too small");
181         }
182         else {
183                 return IkiWiki::SuccessReason->new("file not too small");
184         }
185 } #}}}
186
187 sub match_ispage ($$;@) { #{{{
188         my $filename=shift;
189
190         if (defined IkiWiki::pagetype($filename)) {
191                 return IkiWiki::SuccessReason->new("file is a wiki page");
192         }
193         else {
194                 return IkiWiki::FailReason->new("file is not a wiki page");
195         }
196 } #}}}
197
198 1