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