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