]> sipb.mit.edu Git - ikiwiki.git/blob - IkiWiki/Plugin/attachment.pm
check attachments in and refresh
[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 $session=$params{session};
58
59                 my $filename=$q->param('attachment');
60                 if (! defined $filename || ! length $filename) {
61                         # no file, so do nothing
62                         return;
63                 }
64                 
65                 # This is an (apparently undocumented) way to get the name
66                 # of the temp file that CGI writes the upload to.
67                 my $tempfile=$q->tmpFileName($filename);
68                 
69                 # Put the attachment in a subdir of the page it's attached
70                 # to, unless that page is an "index" page.
71                 my $page=$form->field('page');
72                 $page=~s/(^|\/)index//;
73                 $filename=(length $page ? $page."/" : "").IkiWiki::basename($filename);
74                 
75                 # To untaint the filename, escape any hazardous characters,
76                 # and make sure it isn't pruned.
77                 $filename=IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($filename));
78                 if (IkiWiki::file_pruned($filename, $config{srcdir})) {
79                         error(gettext("bad attachment filename"));
80                 }
81                 
82                 # Check that the user is allowed to edit a page with the
83                 # name of the attachment.
84                 IkiWiki::check_canedit($filename, $q, $session, 1);
85                 
86                 # Use a special pagespec to test that the attachment is valid.
87                 my $allowed=1;
88                 foreach my $admin (@{$config{adminuser}}) {
89                         my $allowed_attachments=IkiWiki::userinfo_get($admin, "allowed_attachments");
90                         if (defined $allowed_attachments &&
91                             length $allowed_attachments) {
92                                 $allowed=pagespec_match($filename,
93                                         $allowed_attachments,
94                                         file => $tempfile);
95                                 last if $allowed;
96                         }
97                 }
98                 if (! $allowed) {
99                         error(gettext("attachment rejected")." ($allowed)");
100                 }
101
102                 # Needed for fast_file_copy and for rendering below.
103                 require IkiWiki::Render;
104
105                 # Move the attachment into place.
106                 # Try to use a fast rename; fall back to copying.
107                 IkiWiki::prep_writefile($filename, $config{srcdir});
108                 unlink($config{srcdir}."/".$filename);
109                 if (! rename($tempfile, $config{srcdir}."/".$filename)) {
110                         my $fh=$q->upload('attachment');
111                         if (! defined $fh || ! ref $fh) {
112                                 error("failed to get filehandle");
113                         }
114                         binmode($fh);
115                         print STDERR "copying $filename\n";
116                         writefile($filename, $config{srcdir}, undef, 1, sub {
117                                 IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_);
118                         });
119                 }
120
121                 # Check the attachment in and trigger a wiki refresh.
122                 if ($config{rcs}) {
123                         IkiWiki::rcs_add($filename);
124                         IkiWiki::disable_commit_hook();
125                         IkiWiki::rcs_commit($filename, gettext("attachment upload"),
126                                 IkiWiki::rcs_prepedit($filename),
127                                 $session->param("name"), $ENV{REMOTE_ADDR});
128                         IkiWiki::enable_commit_hook();
129                         IkiWiki::rcs_update();
130                 }
131                 print STDERR "refreshing\n";
132                 IkiWiki::refresh();
133                 IkiWiki::saveindex();
134         }
135 } # }}}
136
137 package IkiWiki::PageSpec;
138
139 sub parsesize ($) { #{{{
140         my $size=shift;
141         no warnings;
142         my $base=$size+0; # force to number
143         use warnings;
144         my $multiple=1;
145         if ($size=~/kb?$/i) {
146                 $multiple=2**10;
147         }
148         elsif ($size=~/mb?$/i) {
149                 $multiple=2**20;
150         }
151         elsif ($size=~/gb?$/i) {
152                 $multiple=2**30;
153         }
154         elsif ($size=~/tb?$/i) {
155                 $multiple=2**40;
156         }
157         return $base * $multiple;
158 } #}}}
159
160 sub match_maxsize ($$;@) { #{{{
161         shift;
162         my $maxsize=eval{parsesize(shift)};
163         if ($@) {
164                 return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)");
165         }
166
167         my %params=@_;
168         if (! exists $params{file}) {
169                 return IkiWiki::FailReason->new("no file specified");
170         }
171
172         if (-s $params{file} > $maxsize) {
173                 return IkiWiki::FailReason->new("file too large");
174         }
175         else {
176                 return IkiWiki::SuccessReason->new("file not too large");
177         }
178 } #}}}
179
180 sub match_minsize ($$;@) { #{{{
181         shift;
182         my $minsize=eval{parsesize(shift)};
183         if ($@) {
184                 return IkiWiki::FailReason->new("unable to parse minsize (or number too large)");
185         }
186
187         my %params=@_;
188         if (! exists $params{file}) {
189                 return IkiWiki::FailReason->new("no file specified");
190         }
191
192         if (-s $params{file} < $minsize) {
193                 return IkiWiki::FailReason->new("file too small");
194         }
195         else {
196                 return IkiWiki::SuccessReason->new("file not too small");
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 } #}}}
210
211 1