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