+ # Move the attachment into holding directory.
+ # Try to use a fast rename; fall back to copying.
+ IkiWiki::prep_writefile($filename, $dest);
+ unlink($dest."/".$filename);
+ if (rename($tempfile, $dest."/".$filename)) {
+ # The temp file has tight permissions; loosen up.
+ chmod(0666 & ~umask, $dest."/".$filename);
+ }
+ else {
+ my $fh=$q->upload('attachment');
+ if (! defined $fh || ! ref $fh) {
+ # needed by old CGI versions
+ $fh=$q->param('attachment');
+ if (! defined $fh || ! ref $fh) {
+ # even that doesn't always work,
+ # fall back to opening the tempfile
+ $fh=undef;
+ open($fh, "<", $tempfile) || error("failed to open \"$tempfile\": $!");
+ }
+ }
+ binmode($fh);
+ require IkiWiki::Render;
+ writefile($filename, $dest, undef, 1, sub {
+ IkiWiki::fast_file_copy($tempfile, $filename, $fh, @_);
+ });
+ }
+
+ json_response($q, $form, $dest."/".$filename, stored_msg());
+}
+
+# Save all stored attachments for a page.
+sub attachments_save {
+ my $form=shift;
+ my $session=shift;
+
+ # Move attachments out of holding directory.
+ my @attachments;
+ my $dir=attachment_holding_location($form->field('page'));
+ foreach my $filename (glob("$dir/*")) {
+ next unless -f $filename;
+ my $dest=$config{srcdir}."/".
+ linkpage(IkiWiki::possibly_foolish_untaint(
+ attachment_location($form->field('page')))).
+ IkiWiki::basename($filename);
+ unlink($dest);
+ rename($filename, $dest);
+ push @attachments, $dest;
+ }
+ return unless @attachments;
+ require IkiWiki::Render;
+ IkiWiki::prune($dir);
+
+ # Check the attachments in and trigger a wiki refresh.
+ if ($config{rcs}) {
+ IkiWiki::rcs_add($_) foreach @attachments;
+ IkiWiki::disable_commit_hook();
+ IkiWiki::rcs_commit_staged(
+ message => gettext("attachment upload"),
+ session => $session,
+ );
+ IkiWiki::enable_commit_hook();
+ IkiWiki::rcs_update();
+ }
+ IkiWiki::refresh();
+ IkiWiki::saveindex();
+}
+
+sub attachment_location ($) {