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