From 8e43bc0e0fef57a0770ef69e9aac845ed6078dfa Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 20:01:10 -0400 Subject: Configure CGI.pm to disable file uploads by default. --- IkiWiki/CGI.pm | 7 ++++--- debian/changelog | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm index 015c9ae01..43986789a 100644 --- a/IkiWiki/CGI.pm +++ b/IkiWiki/CGI.pm @@ -667,10 +667,11 @@ sub cgi (;$$) { #{{{ my $q=shift; my $session=shift; + eval q{use CGI}; + error($@) if $@; + $CGI::DISABLE_UPLOADS=1; + if (! $q) { - eval q{use CGI}; - error($@) if $@; - binmode(STDIN); $q=CGI->new; binmode(STDIN, ":utf8"); diff --git a/debian/changelog b/debian/changelog index 9153254b4..8e1ad074c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +ikiwiki (2.52) UNRELEASED; urgency=low + + * Configure CGI.pm to disable file uploads by default. + + -- Joey Hess Mon, 30 Jun 2008 19:56:28 -0400 + ikiwiki (2.51) unstable; urgency=low * Improve toplevel parentlink to link directly to index.html when usedirs is -- cgit v1.2.3 From e1d9747be167c834c5b1b98331f40dc98fc46c93 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 20:33:39 -0400 Subject: remove unused editpage title The title was set to editpage, but then always changed. And some code tested for this. Remove this dead code. --- IkiWiki/CGI.pm | 1 - IkiWiki/Plugin/editdiff.pm | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm index 43986789a..2c4590bab 100644 --- a/IkiWiki/CGI.pm +++ b/IkiWiki/CGI.pm @@ -281,7 +281,6 @@ sub cgi_editpage ($$) { #{{{ eval q{use CGI::FormBuilder}; error($@) if $@; my $form = CGI::FormBuilder->new( - title => "editpage", fields => \@fields, charset => "utf-8", method => 'POST', diff --git a/IkiWiki/Plugin/editdiff.pm b/IkiWiki/Plugin/editdiff.pm index 2a70ca0b8..b8ecaa3d7 100644 --- a/IkiWiki/Plugin/editdiff.pm +++ b/IkiWiki/Plugin/editdiff.pm @@ -48,8 +48,7 @@ sub formbuilder_setup { #{{{ my $form=$params{form}; my $page=$form->field("page"); - return if $form->title ne "editpage" - || $form->field("do") ne "edit"; + return if $form->field("do") ne "edit"; $page = IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($page)); return unless exists $pagesources{$page}; -- cgit v1.2.3 From dbab5e9b85b04c5ae5b8687b954ff0ae96f1c3f4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 21:13:41 -0400 Subject: disable cgi uploads earlier This allows plugins that want to enable uploads to do so by changing the value of $CGI::DISABLE_UPLOADS at some point before the cgi hook is run. --- IkiWiki/CGI.pm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm index 2c4590bab..0d010b115 100644 --- a/IkiWiki/CGI.pm +++ b/IkiWiki/CGI.pm @@ -6,6 +6,8 @@ use IkiWiki; use IkiWiki::UserInfo; use open qw{:utf8 :std}; use Encode; +use CGI; +$CGI::DISABLE_UPLOADS=1; package IkiWiki; @@ -666,10 +668,6 @@ sub cgi (;$$) { #{{{ my $q=shift; my $session=shift; - eval q{use CGI}; - error($@) if $@; - $CGI::DISABLE_UPLOADS=1; - if (! $q) { binmode(STDIN); $q=CGI->new; -- cgit v1.2.3 From 6643db0cd20be4fdc6d06c767b079575e4e757f2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 21:15:19 -0400 Subject: add support for an attachment upload field FormBuilder makes it annoyingly hard to move a submit button to a nonstandard place. The button name has to be "_submit" or FormBuilder will ignore it. --- templates/editpage.tmpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/editpage.tmpl b/templates/editpage.tmpl index b0bb0ecb9..36b616f5b 100644 --- a/templates/editpage.tmpl +++ b/templates/editpage.tmpl @@ -60,6 +60,11 @@ Optional comment about this change:
+ +

+Attach a file: +

+
-- cgit v1.2.3 From 0a37c45f32b6a0cf92eb046deabf933fb48ac197 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 22:58:33 -0400 Subject: basic attachment plugin, unfinished Currently includes UI, and a few tests of the attachment, as well as the framework to extend pagespecs to test attachments. Does not actually save the file yet. --- IkiWiki/Plugin/attachment.pm | 134 +++++++++++++++++++++++++++++++++++++++++++ doc/plugins/attachment.mdwn | 43 ++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 IkiWiki/Plugin/attachment.pm create mode 100644 doc/plugins/attachment.mdwn diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm new file mode 100644 index 000000000..7af399fc0 --- /dev/null +++ b/IkiWiki/Plugin/attachment.pm @@ -0,0 +1,134 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::attachment; + +use warnings; +use strict; +use IkiWiki 2.00; +use CGI; +$CGI::DISABLE_UPLOADS=0; + +# TODO move to admin prefs +$config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (* and maxsize(.50kb))"; + +sub import { #{{{ + hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup); + hook(type => "formbuilder", id => "attachment", call => \&formbuilder); +} # }}} + +sub formbuilder_setup { #{{{ + my %params=@_; + my $form=$params{form}; + + return if $form->field("do") ne "edit"; + + $form->field(name => 'attachment', type => 'file'); +} #}}} + +sub formbuilder (@) { #{{{ + my %params=@_; + my $form=$params{form}; + + return if $form->field("do") ne "edit"; + + if ($form->submitted eq "Upload") { + my $q=$params{cgi}; + my $filename=IkiWiki::basename($q->param('attachment')); + if (! defined $filename || ! length $filename) { + # no file, so do nothing + return; + } + + # This is an (apparently undocumented) way to get the name + # of the temp file that CGI writes the upload to. + my $tempfile=$q->tmpFileName($filename); + + # To untaint the filename, escape any hazardous characters, + # and make sure it isn't pruned. + $filename=IkiWiki::possibly_foolish_untaint(IkiWiki::titlepage($filename)); + if (IkiWiki::file_pruned($filename, $config{srcdir})) { + error(gettext("bad attachment filename")); + } + + # Check that the attachment matches the configured + # pagespec. + my $result=pagespec_match($filename, $config{valid_attachments}, + tempfile => $tempfile); + if (! $result) { + error(gettext("attachment rejected")." ($result)"); + } + + my $fh=$q->upload('attachment'); + if (! defined $fh || ! ref $fh) { + error("failed to get filehandle"); + } + binmode($fh); + while (<$fh>) { + print STDERR $_."\n"; + } + } +} # }}} + +package IkiWiki::PageSpec; + +sub parsesize { #{{{ + my $size=shift; + no warnings; + my $base=$size+0; # force to number + use warnings; + my $exponent=1; + if ($size=~/kb?$/i) { + $exponent=10; + } + elsif ($size=~/mb?$/i) { + $exponent=20; + } + elsif ($size=~/gb?$/i) { + $exponent=30; + } + elsif ($size=~/tb?$/i) { + $exponent=40; + } + return $base * 2**$exponent; +} #}}} + +sub match_maxsize ($$;@) { #{{{ + shift; + my $maxsize=eval{parsesize(shift)}; + if ($@) { + return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)"); + } + + my %params=@_; + if (! exists $params{tempfile}) { + return IkiWiki::FailReason->new("no tempfile specified"); + } + + if (-s $params{tempfile} > $maxsize) { + return IkiWiki::FailReason->new("attachment too large"); + } + else { + return IkiWiki::SuccessReason->new("attachment size ok"); + } +} #}}} + +sub match_minsize ($$;@) { #{{{ + shift; + my $minsize=eval{parsesize(shift)}; + if ($@) { + return IkiWiki::FailReason->new("unable to parse minsize (or number too large)"); + } + + my %params=@_; + if (! exists $params{tempfile}) { + return IkiWiki::FailReason->new("no tempfile specified"); + } + + if (-s $params{tempfile} < $minsize) { + return IkiWiki::FailReason->new("attachment too small"); + } + else { + return IkiWiki::SuccessReason->new("attachment size ok"); + } +} #}}} + +1 diff --git a/doc/plugins/attachment.mdwn b/doc/plugins/attachment.mdwn new file mode 100644 index 000000000..a03865987 --- /dev/null +++ b/doc/plugins/attachment.mdwn @@ -0,0 +1,43 @@ +[[template id=plugin name=conditional core=1 author="[[Joey]]"]] +[[tag type/useful]] + +This plugin allows files to be uploaded to the wiki over the web. + +For each page `foo`, files in the subdirectory `foo/` are treated as +attachments of that page. Attachments can be uploaded and managed as +part of the interface for editing a page. + +Warning: Do not enable this plugin on publically editable wikis, unless you +take care to lock down the types and sizes of files that can be uploaded. +Bear in mind that if you let anyone upload a particular kind of file +("*.mp3" files, say), then someone can abuse your wiki in at least three ways: + +1. By uploading many mp3 files, wasting your disk space. +2. By uploading mp3 files that attempt to exploit security holes + in web browsers or other players. +3. By uploading files that claim to be mp3 files, but are really some + other kind of file. Some web browsers may display a `foo.mp3` that + contains html as a web page; including running any malicious javascript + embedded in that page. + +To provide a way to combat these abuses, the wiki admin can specify a +[[ikiwiki/PageSpec]] on their preferences page, to control what types of +attachments can be uploaded. The regular [[ikiwiki/PageSpec]] syntax is +expanded with additional tests. + +For example, to limit arbitrary files to 50 kilobtes, but allow +larger mp3 files to be uploaded, a test like this could be used: + + (*.mp3 and maxsize(15mb)) or (* and maxsize(50kb)) + +The following additional tests are available: + +* maxsize(size) + + Tests whether the attachment is no larger than the specified size. + The size defaults to being in bytes, but "kb", "mb", "gb" etc can be + used to specify the units. + +* minsize(size) + + Tests whether the attachment is no smaller than the specified size. -- cgit v1.2.3 From 622033f5d64604762736eaadd1d3e6f3121b645c Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 23:04:37 -0400 Subject: minor improvements --- IkiWiki/Plugin/attachment.pm | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 7af399fc0..48a1c58b4 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -8,7 +8,7 @@ use CGI; $CGI::DISABLE_UPLOADS=0; # TODO move to admin prefs -$config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (* and maxsize(.50kb))"; +$config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (* and maxsize(50kb))"; sub import { #{{{ hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup); @@ -49,12 +49,14 @@ sub formbuilder (@) { #{{{ error(gettext("bad attachment filename")); } - # Check that the attachment matches the configured - # pagespec. - my $result=pagespec_match($filename, $config{valid_attachments}, - tempfile => $tempfile); - if (! $result) { - error(gettext("attachment rejected")." ($result)"); + # Use a pagespec to test that the attachment is valid. + if (exists $config{valid_attachments} && + length $config{valid_attachments}) { + my $result=pagespec_match($filename, $config{valid_attachments}, + tempfile => $tempfile); + if (! $result) { + error(gettext("attachment rejected")." ($result)"); + } } my $fh=$q->upload('attachment'); @@ -75,20 +77,20 @@ sub parsesize { #{{{ no warnings; my $base=$size+0; # force to number use warnings; - my $exponent=1; + my $multiple=1; if ($size=~/kb?$/i) { - $exponent=10; + $multiple=2**10; } elsif ($size=~/mb?$/i) { - $exponent=20; + $multiple=2**20; } elsif ($size=~/gb?$/i) { - $exponent=30; + $multiple=2**30; } elsif ($size=~/tb?$/i) { - $exponent=40; + $multiple=2**40; } - return $base * 2**$exponent; + return $base * $multiple; } #}}} sub match_maxsize ($$;@) { #{{{ -- cgit v1.2.3 From b01ee9b3b33daa8d305017aa914913f3dac20ce5 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 23:17:01 -0400 Subject: add an ispage limit --- IkiWiki/Plugin/attachment.pm | 33 ++++++++++++++++++++++----------- doc/plugins/attachment.mdwn | 12 +++++++++++- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 48a1c58b4..186f3ea21 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -8,7 +8,7 @@ use CGI; $CGI::DISABLE_UPLOADS=0; # TODO move to admin prefs -$config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (* and maxsize(50kb))"; +$config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (!ispage() and maxsize(50kb))"; sub import { #{{{ hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup); @@ -48,12 +48,12 @@ sub formbuilder (@) { #{{{ if (IkiWiki::file_pruned($filename, $config{srcdir})) { error(gettext("bad attachment filename")); } - + # Use a pagespec to test that the attachment is valid. if (exists $config{valid_attachments} && length $config{valid_attachments}) { my $result=pagespec_match($filename, $config{valid_attachments}, - tempfile => $tempfile); + file => $tempfile); if (! $result) { error(gettext("attachment rejected")." ($result)"); } @@ -101,15 +101,15 @@ sub match_maxsize ($$;@) { #{{{ } my %params=@_; - if (! exists $params{tempfile}) { + if (! exists $params{file}) { return IkiWiki::FailReason->new("no tempfile specified"); } - if (-s $params{tempfile} > $maxsize) { - return IkiWiki::FailReason->new("attachment too large"); + if (-s $params{file} > $maxsize) { + return IkiWiki::FailReason->new("file too large"); } else { - return IkiWiki::SuccessReason->new("attachment size ok"); + return IkiWiki::SuccessReason->new("file not too large"); } } #}}} @@ -121,15 +121,26 @@ sub match_minsize ($$;@) { #{{{ } my %params=@_; - if (! exists $params{tempfile}) { + if (! exists $params{file}) { return IkiWiki::FailReason->new("no tempfile specified"); } - if (-s $params{tempfile} < $minsize) { - return IkiWiki::FailReason->new("attachment too small"); + if (-s $params{file} < $minsize) { + return IkiWiki::FailReason->new("file too small"); + } + else { + return IkiWiki::SuccessReason->new("file not too small"); + } +} #}}} + +sub match_ispage ($$;@) { #{{{ + my $filename=shift; + + if (IkiWiki::pagetype($filename)) { + return IkiWiki::SuccessReason->new("file is a wiki page"); } else { - return IkiWiki::SuccessReason->new("attachment size ok"); + return IkiWiki::FailReason->new("file is not a wiki page"); } } #}}} diff --git a/doc/plugins/attachment.mdwn b/doc/plugins/attachment.mdwn index a03865987..019d1c9e4 100644 --- a/doc/plugins/attachment.mdwn +++ b/doc/plugins/attachment.mdwn @@ -28,7 +28,7 @@ expanded with additional tests. For example, to limit arbitrary files to 50 kilobtes, but allow larger mp3 files to be uploaded, a test like this could be used: - (*.mp3 and maxsize(15mb)) or (* and maxsize(50kb)) + (*.mp3 and maxsize(15mb)) or (!ispage() and maxsize(50kb)) The following additional tests are available: @@ -41,3 +41,13 @@ The following additional tests are available: * minsize(size) Tests whether the attachment is no smaller than the specified size. + +* ispage() + + Tests whether the attachment will be treated by ikiwiki as a wiki page. + (Ie, if it has an extension of ".mdwn", or of any other enabled page + format). + + So, if you don't want to allow wiki pages to be uploaded as attachments, + use `!ispage()` ; if you only want to allow wiki pages to be uploaded + as attachments, use `ispage()`. -- cgit v1.2.3 From 49514f39f64aec9c361f252d07de8d81d38fac9f Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 23:32:08 -0400 Subject: fix some messages --- IkiWiki/Plugin/attachment.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 186f3ea21..6168ea589 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -102,7 +102,7 @@ sub match_maxsize ($$;@) { #{{{ my %params=@_; if (! exists $params{file}) { - return IkiWiki::FailReason->new("no tempfile specified"); + return IkiWiki::FailReason->new("no file specified"); } if (-s $params{file} > $maxsize) { @@ -122,7 +122,7 @@ sub match_minsize ($$;@) { #{{{ my %params=@_; if (! exists $params{file}) { - return IkiWiki::FailReason->new("no tempfile specified"); + return IkiWiki::FailReason->new("no file specified"); } if (-s $params{file} < $minsize) { -- cgit v1.2.3 From 2ca1e12c5f8d74d0604150f25955e0d32529e5b8 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 30 Jun 2008 23:38:07 -0400 Subject: changelog --- debian/changelog | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 8e1ad074c..152689948 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,9 @@ ikiwiki (2.52) UNRELEASED; urgency=low - * Configure CGI.pm to disable file uploads by default. + * attachment: New plugin for uploading and managing attachments. + (Sponsored by The TOVA Company.) + * If attachments are not enabled, configure CGI.pm to disable file + uploads by default. (An anti-DOS measure.) -- Joey Hess Mon, 30 Jun 2008 19:56:28 -0400 -- cgit v1.2.3 From 0f8ea7ecca27a9e73af5c515f637f543912c7076 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 00:42:23 -0400 Subject: break out fast_file_copy --- IkiWiki/Render.pm | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/IkiWiki/Render.pm b/IkiWiki/Render.pm index 272eb239a..2cdec421f 100644 --- a/IkiWiki/Render.pm +++ b/IkiWiki/Render.pm @@ -180,6 +180,30 @@ sub scan ($) { #{{{ } } #}}} +sub fast_file_copy ($$$$$) { #{{{ + my $srcfile=shift; + my $destfile=shift; + my $srcfd=shift; + my $destfd=shift; + my $cleanup=shift; + + my $blksize = 16384; + my ($len, $buf, $written); + while ($len = sysread $srcfd, $buf, $blksize) { + if (! defined $len) { + next if $! =~ /^Interrupted/; + error("failed to read $srcfile: $!", $cleanup); + } + my $offset = 0; + while ($len) { + defined($written = syswrite $destfd, $buf, $len, $offset) + or error("failed to write $destfile: $!", $cleanup); + $len -= $written; + $offset += $written; + } + } +} + sub render ($) { #{{{ my $file=shift; @@ -215,24 +239,7 @@ sub render ($) { #{{{ my $srcfd=readfile($srcfile, 1, 1); writefile($file, $config{destdir}, undef, 1, sub { - my $destfd=shift; - my $cleanup=shift; - - my $blksize = 16384; - my ($len, $buf, $written); - while ($len = sysread $srcfd, $buf, $blksize) { - if (! defined $len) { - next if $! =~ /^Interrupted/; - error("failed to read $srcfile: $!", $cleanup); - } - my $offset = 0; - while ($len) { - defined($written = syswrite $destfd, $buf, $len, $offset) - or error("failed to write $file: $!", $cleanup); - $len -= $written; - $offset += $written; - } - } + fast_file_copy($srcfile, $file, $srcfd, @_); }); } } #}}} -- cgit v1.2.3 From 4fa115fdb5e31b28d8b7f5dce8c4ed6fedc464cd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 00:42:42 -0400 Subject: copy the attachment into srcdir --- IkiWiki/Plugin/attachment.pm | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 6168ea589..88b26e041 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -6,7 +6,7 @@ use strict; use IkiWiki 2.00; use CGI; $CGI::DISABLE_UPLOADS=0; - + # TODO move to admin prefs $config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (!ispage() and maxsize(50kb))"; @@ -49,6 +49,12 @@ sub formbuilder (@) { #{{{ error(gettext("bad attachment filename")); } + # XXX Put the attachment in a subdir corresponding to the + # page being edited. + # The editpage code has already checked that + # $form->field('page') is valid. + $filename="XXX/$filename"; + # Use a pagespec to test that the attachment is valid. if (exists $config{valid_attachments} && length $config{valid_attachments}) { @@ -58,15 +64,25 @@ sub formbuilder (@) { #{{{ error(gettext("attachment rejected")." ($result)"); } } - - my $fh=$q->upload('attachment'); - if (! defined $fh || ! ref $fh) { - error("failed to get filehandle"); - } - binmode($fh); - while (<$fh>) { - print STDERR $_."\n"; + + # Move the attachment into place. + # Try to use a fast rename; fall back to copying. + prep_writefile($filename, $config{srcdir}); + unlink($config{srcdir}."/".$filename); + if (! rename($tempfile, $config{srcdir}."/".$filename)) { + my $fh=$q->upload('attachment'); + if (! defined $fh || ! ref $fh) { + error("failed to get filehandle"); + } + binmode($fh); + writefile($filename, $config{srcdir}, undef, 1, sub { + IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_); + }); } + + # TODO add to vcs + + # TODO trigger a wiki build if there's no vcs } } # }}} -- cgit v1.2.3 From cec45b69da7a12ff4c8e78a47d887d273d480251 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 00:43:19 -0400 Subject: bugfix; support pagetype of "0" --- IkiWiki/Plugin/attachment.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 88b26e041..82fcdbb74 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -152,7 +152,7 @@ sub match_minsize ($$;@) { #{{{ sub match_ispage ($$;@) { #{{{ my $filename=shift; - if (IkiWiki::pagetype($filename)) { + if (defined IkiWiki::pagetype($filename)) { return IkiWiki::SuccessReason->new("file is a wiki page"); } else { -- cgit v1.2.3 From dbf23748dd3bda9f0c520e302876ef280d71f199 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:20:42 -0400 Subject: call check_canedit Needed to prevent uploads of locked pages as attachments. --- IkiWiki/Plugin/attachment.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 82fcdbb74..8babe2be2 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -65,6 +65,10 @@ sub formbuilder (@) { #{{{ } } + # Also check that the user is allowed to edit it by other + # policies. + IkiWiki::check_canedit($filename, $q, $params{session}, 1); + # Move the attachment into place. # Try to use a fast rename; fall back to copying. prep_writefile($filename, $config{srcdir}); -- cgit v1.2.3 From de6ed410bc8edfd558a62eac4536a716027da114 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:22:11 -0400 Subject: reorder canedit should fail first as it's a less expensive and harder to pass test --- IkiWiki/Plugin/attachment.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 8babe2be2..23862bc05 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -54,6 +54,10 @@ sub formbuilder (@) { #{{{ # The editpage code has already checked that # $form->field('page') is valid. $filename="XXX/$filename"; + + # Also check that the user is allowed to edit it by other + # policies. + IkiWiki::check_canedit($filename, $q, $params{session}, 1); # Use a pagespec to test that the attachment is valid. if (exists $config{valid_attachments} && @@ -65,10 +69,6 @@ sub formbuilder (@) { #{{{ } } - # Also check that the user is allowed to edit it by other - # policies. - IkiWiki::check_canedit($filename, $q, $params{session}, 1); - # Move the attachment into place. # Try to use a fast rename; fall back to copying. prep_writefile($filename, $config{srcdir}); -- cgit v1.2.3 From b4296a49438d5cdb3dd8cfd8bb4185c909b1328b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:31:03 -0400 Subject: simplification --- IkiWiki/CGI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm index 0d010b115..2805534fc 100644 --- a/IkiWiki/CGI.pm +++ b/IkiWiki/CGI.pm @@ -305,7 +305,7 @@ sub cgi_editpage ($$) { #{{{ # This untaint is safe because titlepage removes any problematic # characters. - my ($page)=$form->field('page'); + my $page=$form->field('page'); $page=titlepage(possibly_foolish_untaint($page)); if (! defined $page || ! length $page || file_pruned($page, $config{srcdir}) || $page=~/^\//) { -- cgit v1.2.3 From a78a981792e7420783f5064f52100635429922f1 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:31:09 -0400 Subject: attachment location Put the attachment in a subdir of the page it's attached to, unless that page is an "index" page. --- IkiWiki/Plugin/attachment.pm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 23862bc05..f9d7f14f6 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -42,21 +42,21 @@ sub formbuilder (@) { #{{{ # of the temp file that CGI writes the upload to. my $tempfile=$q->tmpFileName($filename); + # Put the attachment in a subdir of the page it's attached + # to, unless that page is an "index" page. + my $page=$form->field('page'); + $page=~s/(^|\/)index//; + $filename="$page/$filename"; + # To untaint the filename, escape any hazardous characters, # and make sure it isn't pruned. - $filename=IkiWiki::possibly_foolish_untaint(IkiWiki::titlepage($filename)); + $filename=IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($filename); if (IkiWiki::file_pruned($filename, $config{srcdir})) { error(gettext("bad attachment filename")); } - # XXX Put the attachment in a subdir corresponding to the - # page being edited. - # The editpage code has already checked that - # $form->field('page') is valid. - $filename="XXX/$filename"; - - # Also check that the user is allowed to edit it by other - # policies. + # Check that the user is allowed to edit a page with the + # name of the attachment. IkiWiki::check_canedit($filename, $q, $params{session}, 1); # Use a pagespec to test that the attachment is valid. -- cgit v1.2.3 From 5aee8d937d694dc5ae43b85c6d81ce9d268d1626 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:39:02 -0400 Subject: bugfixes --- IkiWiki/Plugin/attachment.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index f9d7f14f6..d2989bb8b 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -32,7 +32,7 @@ sub formbuilder (@) { #{{{ if ($form->submitted eq "Upload") { my $q=$params{cgi}; - my $filename=IkiWiki::basename($q->param('attachment')); + my $filename=$q->param('attachment'); if (! defined $filename || ! length $filename) { # no file, so do nothing return; @@ -46,11 +46,11 @@ sub formbuilder (@) { #{{{ # to, unless that page is an "index" page. my $page=$form->field('page'); $page=~s/(^|\/)index//; - $filename="$page/$filename"; + $filename=$page."/".IkiWiki::basename($filename); # To untaint the filename, escape any hazardous characters, # and make sure it isn't pruned. - $filename=IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($filename); + $filename=IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($filename)); if (IkiWiki::file_pruned($filename, $config{srcdir})) { error(gettext("bad attachment filename")); } @@ -71,7 +71,7 @@ sub formbuilder (@) { #{{{ # Move the attachment into place. # Try to use a fast rename; fall back to copying. - prep_writefile($filename, $config{srcdir}); + IkiWiki::prep_writefile($filename, $config{srcdir}); unlink($config{srcdir}."/".$filename); if (! rename($tempfile, $config{srcdir}."/".$filename)) { my $fh=$q->upload('attachment'); -- cgit v1.2.3 From 7cd40ea4eb8955765dc3e61dd3edaf08d1bbab48 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:43:32 -0400 Subject: better approach for cgi upload disabling Make it a config setting, this way subtle load order issues don't come into play. (As much?) --- IkiWiki.pm | 1 + IkiWiki/CGI.pm | 6 ++++-- IkiWiki/Plugin/attachment.pm | 9 ++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/IkiWiki.pm b/IkiWiki.pm index 7d5668d74..d9b3dcdb4 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -88,6 +88,7 @@ sub defaultconfig () { #{{{ account_creation_password => "", prefix_directives => 0, hardlink => 0, + cgi_disable_uploads => 1, } #}}} sub checkconfig () { #{{{ diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm index 2805534fc..07e92322f 100644 --- a/IkiWiki/CGI.pm +++ b/IkiWiki/CGI.pm @@ -6,8 +6,6 @@ use IkiWiki; use IkiWiki::UserInfo; use open qw{:utf8 :std}; use Encode; -use CGI; -$CGI::DISABLE_UPLOADS=1; package IkiWiki; @@ -668,6 +666,10 @@ sub cgi (;$$) { #{{{ my $q=shift; my $session=shift; + eval q{use CGI}; + error($@) if $@; + $CGI::DISABLE_UPLOADS=$config{cgi_disable_uploads}; + if (! $q) { binmode(STDIN); $q=CGI->new; diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index d2989bb8b..106660012 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -4,18 +4,21 @@ package IkiWiki::Plugin::attachment; use warnings; use strict; use IkiWiki 2.00; -use CGI; -$CGI::DISABLE_UPLOADS=0; # TODO move to admin prefs $config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (!ispage() and maxsize(50kb))"; 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); } # }}} -sub formbuilder_setup { #{{{ +sub checkconfig () { #{{{ + $config{cgi_disable_uploads}=0; +} #}}} + +sub formbuilder_setup (@) { #{{{ my %params=@_; my $form=$params{form}; -- cgit v1.2.3 From d7ca2f8f9be00ac05d86c9a3abdba0c8ef3a3800 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:48:07 -0400 Subject: fix expensive move code path --- IkiWiki/Plugin/attachment.pm | 3 +++ IkiWiki/Render.pm | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 106660012..a474f6e75 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -72,6 +72,9 @@ sub formbuilder (@) { #{{{ } } + # Needed for fast_file_copy. + require IkiWiki::Render; + # Move the attachment into place. # Try to use a fast rename; fall back to copying. IkiWiki::prep_writefile($filename, $config{srcdir}); diff --git a/IkiWiki/Render.pm b/IkiWiki/Render.pm index 2cdec421f..5184be2df 100644 --- a/IkiWiki/Render.pm +++ b/IkiWiki/Render.pm @@ -180,7 +180,7 @@ sub scan ($) { #{{{ } } #}}} -sub fast_file_copy ($$$$$) { #{{{ +sub fast_file_copy (@) { #{{{ my $srcfile=shift; my $destfile=shift; my $srcfd=shift; -- cgit v1.2.3 From 00962f589a6a5b97f938e036919cdb4a66ec0538 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 13:51:22 -0400 Subject: minor optimisation --- IkiWiki/Plugin/lockedit.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IkiWiki/Plugin/lockedit.pm b/IkiWiki/Plugin/lockedit.pm index 010705c49..5ffb4e6f9 100644 --- a/IkiWiki/Plugin/lockedit.pm +++ b/IkiWiki/Plugin/lockedit.pm @@ -40,11 +40,11 @@ sub formbuilder_setup (@) { #{{{ my %params=@_; my $form=$params{form}; - my $session=$params{session}; - my $cgi=$params{cgi}; - my $user_name=$session->param("name"); - if ($form->title eq "preferences") { + my $session=$params{session}; + my $cgi=$params{cgi}; + my $user_name=$session->param("name"); + $form->field(name => "locked_pages", size => 50, fieldset => "admin", comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")"); -- cgit v1.2.3 From e87c3ae6df9a8abc49879851a1c70a41c49176ed Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 14:02:30 -0400 Subject: add allowed attachments field to admin prefs --- IkiWiki/Plugin/attachment.pm | 53 ++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index a474f6e75..8d3d60de6 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -5,9 +5,6 @@ use warnings; use strict; use IkiWiki 2.00; -# TODO move to admin prefs -$config{valid_attachments}="(*.mp3 and maxsize(15mb)) or (!ispage() and maxsize(50kb))"; - sub import { #{{{ hook(type => "checkconfig", id => "attachment", call => \&checkconfig); hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup); @@ -22,9 +19,31 @@ sub formbuilder_setup (@) { #{{{ my %params=@_; my $form=$params{form}; - return if $form->field("do") ne "edit"; - - $form->field(name => 'attachment', type => 'file'); + if ($form->field("do") eq "edit") { + $form->field(name => 'attachment', type => 'file'); + } + elsif ($form->title eq "preferences") { + my $session=$params{session}; + my $user_name=$session->param("name"); + + $form->field(name => "allowed_attachments", size => 50, + fieldset => "admin", + comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")"); + if (! IkiWiki::is_admin($user_name)) { + $form->field(name => "allowed_attachments", type => "hidden"); + } + if (! $form->submitted) { + $form->field(name => "allowed_attachments", force => 1, + value => IkiWiki::userinfo_get($user_name, "allowed_attachments")); + } + if ($form->submitted && $form->submitted eq 'Save Preferences') { + if (defined $form->field("allowed_attachments")) { + IkiWiki::userinfo_set($user_name, "allowed_attachments", + $form->field("allowed_attachments")) || + error("failed to set allowed_attachments"); + } + } + } } #}}} sub formbuilder (@) { #{{{ @@ -62,15 +81,21 @@ sub formbuilder (@) { #{{{ # name of the attachment. IkiWiki::check_canedit($filename, $q, $params{session}, 1); - # Use a pagespec to test that the attachment is valid. - if (exists $config{valid_attachments} && - length $config{valid_attachments}) { - my $result=pagespec_match($filename, $config{valid_attachments}, - file => $tempfile); - if (! $result) { - error(gettext("attachment rejected")." ($result)"); + # Use a special pagespec to test that the attachment is valid. + my $allowed=1; + foreach my $admin (@{$config{adminuser}}) { + my $allowed_attachments=IkiWiki::userinfo_get($admin, "allowed_attachments"); + if (defined $allowed_attachments && + length $allowed_attachments) { + $allowed=pagespec_match($filename, + $allowed_attachments, + file => $tempfile); + last if $allowed; } } + if (! $allowed) { + error(gettext("attachment rejected")." ($allowed)"); + } # Needed for fast_file_copy. require IkiWiki::Render; @@ -98,7 +123,7 @@ sub formbuilder (@) { #{{{ package IkiWiki::PageSpec; -sub parsesize { #{{{ +sub parsesize ($) { #{{{ my $size=shift; no warnings; my $base=$size+0; # force to number -- cgit v1.2.3 From 1405006fbf8d78e3f629c60a98e01b5254de414e Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 15:35:01 -0400 Subject: check attachments in and refresh --- IkiWiki/Plugin/attachment.pm | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 8d3d60de6..4141cc04a 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -54,6 +54,8 @@ sub formbuilder (@) { #{{{ if ($form->submitted eq "Upload") { my $q=$params{cgi}; + my $session=$params{session}; + my $filename=$q->param('attachment'); if (! defined $filename || ! length $filename) { # no file, so do nothing @@ -68,7 +70,7 @@ sub formbuilder (@) { #{{{ # to, unless that page is an "index" page. my $page=$form->field('page'); $page=~s/(^|\/)index//; - $filename=$page."/".IkiWiki::basename($filename); + $filename=(length $page ? $page."/" : "").IkiWiki::basename($filename); # To untaint the filename, escape any hazardous characters, # and make sure it isn't pruned. @@ -79,7 +81,7 @@ sub formbuilder (@) { #{{{ # Check that the user is allowed to edit a page with the # name of the attachment. - IkiWiki::check_canedit($filename, $q, $params{session}, 1); + IkiWiki::check_canedit($filename, $q, $session, 1); # Use a special pagespec to test that the attachment is valid. my $allowed=1; @@ -97,7 +99,7 @@ sub formbuilder (@) { #{{{ error(gettext("attachment rejected")." ($allowed)"); } - # Needed for fast_file_copy. + # Needed for fast_file_copy and for rendering below. require IkiWiki::Render; # Move the attachment into place. @@ -110,14 +112,25 @@ sub formbuilder (@) { #{{{ error("failed to get filehandle"); } binmode($fh); + print STDERR "copying $filename\n"; writefile($filename, $config{srcdir}, undef, 1, sub { IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_); }); } - # TODO add to vcs - - # TODO trigger a wiki build if there's no vcs + # Check the attachment in and trigger a wiki refresh. + if ($config{rcs}) { + IkiWiki::rcs_add($filename); + IkiWiki::disable_commit_hook(); + IkiWiki::rcs_commit($filename, gettext("attachment upload"), + IkiWiki::rcs_prepedit($filename), + $session->param("name"), $ENV{REMOTE_ADDR}); + IkiWiki::enable_commit_hook(); + IkiWiki::rcs_update(); + } + print STDERR "refreshing\n"; + IkiWiki::refresh(); + IkiWiki::saveindex(); } } # }}} -- cgit v1.2.3 From 14cb2b0869496105915892b509016b9e46991ee7 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 16:13:47 -0400 Subject: save attachments when page is saved too A user might specify an attachment, but not click the upload button, and just save the whole page instead. --- IkiWiki/Plugin/attachment.pm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 4141cc04a..48cd954d9 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -52,7 +52,7 @@ sub formbuilder (@) { #{{{ return if $form->field("do") ne "edit"; - if ($form->submitted eq "Upload") { + if ($form->submitted eq "Upload" || $form->submitted eq "Save Page") { my $q=$params{cgi}; my $session=$params{session}; @@ -112,7 +112,6 @@ sub formbuilder (@) { #{{{ error("failed to get filehandle"); } binmode($fh); - print STDERR "copying $filename\n"; writefile($filename, $config{srcdir}, undef, 1, sub { IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_); }); @@ -128,7 +127,6 @@ sub formbuilder (@) { #{{{ IkiWiki::enable_commit_hook(); IkiWiki::rcs_update(); } - print STDERR "refreshing\n"; IkiWiki::refresh(); IkiWiki::saveindex(); } -- cgit v1.2.3 From c1e9e121b738909a84eba8be327b5eaca74e7964 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 17:19:38 -0400 Subject: basic attachment list --- IkiWiki/Plugin/attachment.pm | 51 ++++++++++++++++++++++++++++++++++++-------- templates/editpage.tmpl | 16 +++++++++++--- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 48cd954d9..1d392589e 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -15,12 +15,50 @@ sub checkconfig () { #{{{ $config{cgi_disable_uploads}=0; } #}}} +sub attachment_location ($) { + my $page=shift; + + # Put the attachment in a subdir of the page it's attached + # to, unless that page is an "index" page. + $page=~s/(^|\/)index//; + $page.="/" if length $page; + + return $page; +} + +sub attachment_list ($) { + my $loc=attachment_location(shift); + + my @ret; + foreach my $f (values %pagesources) { + print STDERR ">>$f\n" if ! defined IkiWiki::pagetype($f); + if (! defined IkiWiki::pagetype($f) && + $f=~m/^\Q$loc\E[^\/]+$/ && + -e "$config{srcdir}/$f") { + push @ret, { + "field-select" => '', + link => $f, + size => (stat(_))[7], + mtime => displaytime($IkiWiki::pagemtime{$f}), + }; + } + } + + return @ret; +} + sub formbuilder_setup (@) { #{{{ my %params=@_; my $form=$params{form}; if ($form->field("do") eq "edit") { $form->field(name => 'attachment', type => 'file'); + $form->tmpl_param("attachment_list" => [attachment_list($form->field('page'))]); + + # These buttons are not put in the usual place, so + # is not added to the normal formbuilder button list. + $form->tmpl_param("field-upload" => ''); + $form->tmpl_param("field-link" => ''); } elsif ($form->title eq "preferences") { my $session=$params{session}; @@ -66,15 +104,10 @@ sub formbuilder (@) { #{{{ # of the temp file that CGI writes the upload to. my $tempfile=$q->tmpFileName($filename); - # Put the attachment in a subdir of the page it's attached - # to, unless that page is an "index" page. - my $page=$form->field('page'); - $page=~s/(^|\/)index//; - $filename=(length $page ? $page."/" : "").IkiWiki::basename($filename); - - # To untaint the filename, escape any hazardous characters, - # and make sure it isn't pruned. - $filename=IkiWiki::titlepage(IkiWiki::possibly_foolish_untaint($filename)); + $filename=IkiWiki::titlepage( + IkiWiki::possibly_foolish_untaint( + attachment_location($form->field('page')). + IkiWiki::basename($filename))); if (IkiWiki::file_pruned($filename, $config{srcdir})) { error(gettext("bad attachment filename")); } diff --git a/templates/editpage.tmpl b/templates/editpage.tmpl index 36b616f5b..1a27beb6f 100644 --- a/templates/editpage.tmpl +++ b/templates/editpage.tmpl @@ -61,9 +61,19 @@ Optional comment about this change:
-

-Attach a file: -

+
+
+ + + + + + + + + +
Attachments
+
-- cgit v1.2.3 From 0ffcafc1305de5ec242f3f0835064682d408c3d0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 17:54:23 -0400 Subject: attachment doc updates --- doc/plugins/contrib/attach/discussion.mdwn | 18 ++++++++++++++++++ doc/soc.mdwn | 2 +- doc/todo/attachments.mdwn | 20 ++++++++++++++++++++ doc/todo/attachments_plugin.mdwn | 1 - doc/todo/fileupload.mdwn | 2 +- 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 doc/plugins/contrib/attach/discussion.mdwn create mode 100644 doc/todo/attachments.mdwn delete mode 100644 doc/todo/attachments_plugin.mdwn diff --git a/doc/plugins/contrib/attach/discussion.mdwn b/doc/plugins/contrib/attach/discussion.mdwn new file mode 100644 index 000000000..803b7dcdb --- /dev/null +++ b/doc/plugins/contrib/attach/discussion.mdwn @@ -0,0 +1,18 @@ +I found this posted to todo list, moved here: --[[Joey]] + +> First pass at an attachments plugin. See [[plugins/contrib/attach]] for +> details/docs. Here's the [diff](http://pastebin.com/f4d889b65), and +> here's some [technical notes](http://pastebin.com/f584b9d9d). There are +> still various things I want to fix and tweak, but it works reasonably for +> me as is. + +I guess I missed this when the plugin page was posted last September, and +since the [[soc]] stuff wasn't updated, I didn't realize this was Ben's soc +work. Which is more or less why I didn't look at it. + +This plugin would need quite a lot of work to finish up, I do think it was +taking the right approach, sorry I never followed up on it. + +In the meantime, I've written an attachment plugin that does most of the +same stuff, and behaves closer to how I originally sketched [[todo/fileupload]] +as working. diff --git a/doc/soc.mdwn b/doc/soc.mdwn index c762d2e43..fffb5bed4 100644 --- a/doc/soc.mdwn +++ b/doc/soc.mdwn @@ -11,7 +11,7 @@ accepted, and the following projects were worked on: (See [[todo/latex]]) * Implement File Upload Functionality and Image Gallery Creation by Ben Coffey - (See [[todo/fileupload/soc-proposal]]) + (See [[todo/fileupload/soc-proposal]] and [[plugins/contrib/attach]]) * Wiki WYSIWYG Editor by [[TaylorKillian]] (See [[todo/wikiwyg]]) diff --git a/doc/todo/attachments.mdwn b/doc/todo/attachments.mdwn new file mode 100644 index 000000000..de7d81400 --- /dev/null +++ b/doc/todo/attachments.mdwn @@ -0,0 +1,20 @@ +Stuff the [[plugins/attachment]] plugin is currently missing, that might be +nice to add: + +* `user()` pagespecs, to limit what individual users can do. (See examples + in [[fileupload]]. +* `mimetype()` pagespecs. (Using a mime type sniffer.)a +* Virus scanning. +* Make the attachments part of the Edit page hidden unless something is + clicked on to display it. To avoid clutter. +* Add a progress bar for attachment uploads (needs AJAX stuff..) +* Maybe optimise the "Insert Links" button with javascript, so, if + javascript is available, the link is inserted at the current cursor + position in the page edit form, without actually reposting the form. + (Falling back to the current reposting of the form if javascript is not + available of course.) +* Set `$CGI::POST_MAX` to some sane value (ie, larger than the largest + configured `maxsize()` in the pagespec, or if none is configured, + something reasonable. Just as a belt-and-suspenders DOS prevention. +* Only allow attachments to be added to a given list of pages. + Maybe a pagespec like `parent(patches/*)` diff --git a/doc/todo/attachments_plugin.mdwn b/doc/todo/attachments_plugin.mdwn deleted file mode 100644 index 3b050b43e..000000000 --- a/doc/todo/attachments_plugin.mdwn +++ /dev/null @@ -1 +0,0 @@ -First pass at an attachments plugin. See [[plugins/contrib/attach]] for details/docs. Here's the [diff](http://pastebin.com/f4d889b65), and here's some [technical notes](http://pastebin.com/f584b9d9d). There are still various things I want to fix and tweak, but it works reasonably for me as is. \ No newline at end of file diff --git a/doc/todo/fileupload.mdwn b/doc/todo/fileupload.mdwn index 1962d6b40..9a9106229 100644 --- a/doc/todo/fileupload.mdwn +++ b/doc/todo/fileupload.mdwn @@ -60,4 +60,4 @@ pagespec lock like the above prevents an edit or upload from happening, ikiwiki could display a reasonable message to the user, indicating what they've done wrong.) -[[tag soc]] +[[tag soc done]] -- cgit v1.2.3 From 8441d3153506bc704d97def187c7a77c10fa7e3f Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 18:08:31 -0400 Subject: add both kinds of links --- IkiWiki/Plugin/attachment.pm | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 1d392589e..690b61cbc 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -27,17 +27,17 @@ sub attachment_location ($) { } sub attachment_list ($) { - my $loc=attachment_location(shift); + my $page=shift; + my $loc=attachment_location($page); my @ret; foreach my $f (values %pagesources) { - print STDERR ">>$f\n" if ! defined IkiWiki::pagetype($f); if (! defined IkiWiki::pagetype($f) && $f=~m/^\Q$loc\E[^\/]+$/ && -e "$config{srcdir}/$f") { push @ret, { "field-select" => '', - link => $f, + link => htmllink($page, $page, $f, noimageinline => 1), size => (stat(_))[7], mtime => displaytime($IkiWiki::pagemtime{$f}), }; @@ -87,11 +87,11 @@ sub formbuilder_setup (@) { #{{{ sub formbuilder (@) { #{{{ my %params=@_; my $form=$params{form}; + my $q=$params{cgi}; return if $form->field("do") ne "edit"; if ($form->submitted eq "Upload" || $form->submitted eq "Save Page") { - my $q=$params{cgi}; my $session=$params{session}; my $filename=$q->param('attachment'); @@ -163,6 +163,15 @@ sub formbuilder (@) { #{{{ IkiWiki::refresh(); IkiWiki::saveindex(); } + elsif ($form->submitted eq "Insert Links") { + my $add=""; + foreach my $f ($q->param("attachment_select")) { + $add.="[[$f]]\n"; + } + $form->field(name => 'editcontent', + value => $form->field('editcontent')."\n\n".$add, + force => 1); + } } # }}} package IkiWiki::PageSpec; -- cgit v1.2.3 From e8a13c32ba090e4428fcc1d25e4f93cd14380326 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 18:40:42 -0400 Subject: display human sizes --- IkiWiki/Plugin/attachment.pm | 70 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 690b61cbc..a8e50f833 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -38,7 +38,7 @@ sub attachment_list ($) { push @ret, { "field-select" => '', link => htmllink($page, $page, $f, noimageinline => 1), - size => (stat(_))[7], + size => humansize((stat(_))[7]), mtime => displaytime($IkiWiki::pagemtime{$f}), }; } @@ -174,32 +174,70 @@ sub formbuilder (@) { #{{{ } } # }}} -package IkiWiki::PageSpec; +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; - my $multiple=1; - if ($size=~/kb?$/i) { - $multiple=2**10; - } - elsif ($size=~/mb?$/i) { - $multiple=2**20; - } - elsif ($size=~/gb?$/i) { - $multiple=2**30; + foreach my $unit (sort keys %units) { + if ($size=~/\Q$unit\E/i) { + return $base * $units{$unit}; + } } - elsif ($size=~/tb?$/i) { - $multiple=2**40; + 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} * 100)/100)."$unit"; + } } - return $base * $multiple; + return $size; # near zero, or negative } #}}} +package IkiWiki::PageSpec; + sub match_maxsize ($$;@) { #{{{ shift; - my $maxsize=eval{parsesize(shift)}; + my $maxsize=eval{IkiWiki::Plugin::attachment::parsesize(shift)}; if ($@) { return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)"); } @@ -219,7 +257,7 @@ sub match_maxsize ($$;@) { #{{{ sub match_minsize ($$;@) { #{{{ shift; - my $minsize=eval{parsesize(shift)}; + my $minsize=eval{IkiWiki::Plugin::attachment::parsesize(shift)}; if ($@) { return IkiWiki::FailReason->new("unable to parse minsize (or number too large)"); } -- cgit v1.2.3 From 0abaf5aaae14efafb4610bfc3b35d0bb111089fb Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 18:41:30 -0400 Subject: only show one sig fig --- IkiWiki/Plugin/attachment.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index a8e50f833..15c9a8c48 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -227,7 +227,7 @@ sub humansize ($) { #{{{ 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} * 100)/100)."$unit"; + return (int($size / $units{$unit} * 10)/10)."$unit"; } } return $size; # near zero, or negative -- cgit v1.2.3 From 883da338a468cff3a03d1627f3a7f7ea7afe56d6 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 19:05:15 -0400 Subject: some fit and finish fixes --- IkiWiki/Plugin/attachment.pm | 93 +++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 15c9a8c48..38c241350 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -15,46 +15,12 @@ sub checkconfig () { #{{{ $config{cgi_disable_uploads}=0; } #}}} -sub attachment_location ($) { - my $page=shift; - - # Put the attachment in a subdir of the page it's attached - # to, unless that page is an "index" page. - $page=~s/(^|\/)index//; - $page.="/" if length $page; - - return $page; -} - -sub attachment_list ($) { - my $page=shift; - my $loc=attachment_location($page); - - my @ret; - foreach my $f (values %pagesources) { - if (! defined IkiWiki::pagetype($f) && - $f=~m/^\Q$loc\E[^\/]+$/ && - -e "$config{srcdir}/$f") { - push @ret, { - "field-select" => '', - link => htmllink($page, $page, $f, noimageinline => 1), - size => humansize((stat(_))[7]), - mtime => displaytime($IkiWiki::pagemtime{$f}), - }; - } - } - - return @ret; -} - sub formbuilder_setup (@) { #{{{ my %params=@_; my $form=$params{form}; if ($form->field("do") eq "edit") { $form->field(name => 'attachment', type => 'file'); - $form->tmpl_param("attachment_list" => [attachment_list($form->field('page'))]); - # These buttons are not put in the usual place, so # is not added to the normal formbuilder button list. $form->tmpl_param("field-upload" => ''); @@ -91,14 +57,10 @@ sub formbuilder (@) { #{{{ return if $form->field("do") ne "edit"; - if ($form->submitted eq "Upload" || $form->submitted eq "Save Page") { + my $filename=$q->param('attachment'); + if (defined $filename && length $filename && + ($form->submitted eq "Upload Attachment" || $form->submitted eq "Save Page")) { my $session=$params{session}; - - my $filename=$q->param('attachment'); - if (! defined $filename || ! length $filename) { - # no file, so do nothing - return; - } # This is an (apparently undocumented) way to get the name # of the temp file that CGI writes the upload to. @@ -139,7 +101,11 @@ sub formbuilder (@) { #{{{ # Try to use a fast rename; fall back to copying. IkiWiki::prep_writefile($filename, $config{srcdir}); unlink($config{srcdir}."/".$filename); - if (! rename($tempfile, $config{srcdir}."/".$filename)) { + if (rename($tempfile, $config{srcdir}."/".$filename)) { + # The temp file has tight permissions; loosen up. + chmod(0666 & ~umask, $config{srcdir}."/".$filename); + } + else { my $fh=$q->upload('attachment'); if (! defined $fh || ! ref $fh) { error("failed to get filehandle"); @@ -172,8 +138,47 @@ sub formbuilder (@) { #{{{ value => $form->field('editcontent')."\n\n".$add, force => 1); } + + # Generate the attachment list only after having added any new + # attachments. + $form->tmpl_param("attachment_list" => [attachment_list($form->field('page'))]); } # }}} +sub attachment_location ($) { + my $page=shift; + + # Put the attachment in a subdir of the page it's attached + # to, unless that page is an "index" page. + $page=~s/(^|\/)index//; + $page.="/" if length $page; + + return $page; +} + +sub attachment_list ($) { + my $page=shift; + my $loc=attachment_location($page); + + my @ret; + foreach my $f (values %pagesources) { + if (! defined IkiWiki::pagetype($f) && + $f=~m/^\Q$loc\E[^\/]+$/ && + -e "$config{srcdir}/$f") { + push @ret, { + "field-select" => '', + link => htmllink($page, $page, $f, noimageinline => 1), + size => humansize((stat(_))[7]), + mtime => displaytime($IkiWiki::pagemtime{$f}), + mtime_raw => $IkiWiki::pagemtime{$f}, + }; + } + } + + # Sort newer attachments to the top of the list, so a newly-added + # attachment appears just before the form used to add it. + return sort { $b->{mtime_raw} <=> $a->{mtime_raw} || $a->{link} cmp $b->{link} } @ret; +} + my %units=( # size in bytes B => 1, byte => 1, @@ -215,7 +220,7 @@ sub parsesize ($) { #{{{ my $base=$size+0; # force to number use warnings; foreach my $unit (sort keys %units) { - if ($size=~/\Q$unit\E/i) { + if ($size=~/\d\Q$unit\E$/i) { return $base * $units{$unit}; } } @@ -248,7 +253,7 @@ sub match_maxsize ($$;@) { #{{{ } if (-s $params{file} > $maxsize) { - return IkiWiki::FailReason->new("file too large"); + return IkiWiki::FailReason->new("file too large (".(-s $params{file})." > $maxsize)"); } else { return IkiWiki::SuccessReason->new("file not too large"); -- cgit v1.2.3 From ed25c2abf259ad1e4f307f0656175a90906b93ba Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 1 Jul 2008 20:35:54 -0400 Subject: fixes --- IkiWiki/Plugin/attachment.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index 38c241350..ae06922d4 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -22,7 +22,7 @@ sub formbuilder_setup (@) { #{{{ if ($form->field("do") eq "edit") { $form->field(name => 'attachment', type => 'file'); # These buttons are not put in the usual place, so - # is not added to the normal formbuilder button list. + # are not added to the normal formbuilder button list. $form->tmpl_param("field-upload" => ''); $form->tmpl_param("field-link" => ''); } @@ -220,7 +220,7 @@ sub parsesize ($) { #{{{ my $base=$size+0; # force to number use warnings; foreach my $unit (sort keys %units) { - if ($size=~/\d\Q$unit\E$/i) { + if ($size=~/[0-9\s]\Q$unit\E$/i) { return $base * $units{$unit}; } } @@ -232,7 +232,7 @@ sub humansize ($) { #{{{ 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 (int($size / $units{$unit} * 10)/10).$unit; } } return $size; # near zero, or negative -- cgit v1.2.3 From e9982352406c2c10a8d36f8df60d44d12c2dbc3d Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 2 Jul 2008 15:33:52 -0400 Subject: export javascript, and support default-open toggles --- IkiWiki/Plugin/toggle.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/IkiWiki/Plugin/toggle.pm b/IkiWiki/Plugin/toggle.pm index 8089443e0..54c9a0d9a 100644 --- a/IkiWiki/Plugin/toggle.pm +++ b/IkiWiki/Plugin/toggle.pm @@ -9,7 +9,7 @@ use IkiWiki 2.00; # of css to hide toggleables, to avoid any flashing on page load. The css # is only emitted after the javascript tests that it's going to be able to # show the toggleables. -my $javascript=<<'EOF'; +our $javascript=<<'EOF';