summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Attachment.pm12
-rw-r--r--Bugzilla/Config/Attachment.pm5
-rwxr-xr-xattachment.cgi83
-rw-r--r--template/en/default/admin/params/attachment.html.tmpl4
-rw-r--r--template/en/default/attachment/confirm-delete.html.tmpl91
-rw-r--r--template/en/default/attachment/delete_reason.txt.tmpl33
-rw-r--r--template/en/default/attachment/edit.html.tmpl16
-rw-r--r--template/en/default/attachment/list.html.tmpl8
-rw-r--r--template/en/default/global/user-error.html.tmpl4
9 files changed, 247 insertions, 9 deletions
diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm
index 350adfd72..00c7ee470 100644
--- a/Bugzilla/Attachment.pm
+++ b/Bugzilla/Attachment.pm
@@ -339,16 +339,16 @@ sub datasize {
# If we have already retrieved the data, return its size.
return length($self->{data}) if exists $self->{data};
- ($self->{datasize}) =
+ $self->{datasize} =
Bugzilla->dbh->selectrow_array("SELECT LENGTH(thedata)
FROM attach_data
WHERE id = ?",
- undef,
- $self->{id});
+ undef, $self->{id}) || 0;
- # If there's no attachment data in the database, the attachment
- # is stored in a local file, so retrieve its size from the file.
- if ($self->{datasize} == 0) {
+ # If there's no attachment data in the database, either the attachment
+ # is stored in a local file, and so retrieve its size from the file,
+ # or the attachment has been deleted.
+ unless ($self->{datasize}) {
if (open(AH, $self->_get_local_filename())) {
binmode AH;
$self->{datasize} = (stat(AH))[7];
diff --git a/Bugzilla/Config/Attachment.pm b/Bugzilla/Config/Attachment.pm
index 449908528..bbaaaa63d 100644
--- a/Bugzilla/Config/Attachment.pm
+++ b/Bugzilla/Config/Attachment.pm
@@ -41,6 +41,11 @@ sub get_param_list {
my $class = shift;
my @param_list = (
{
+ name => 'allow_attachment_deletion',
+ type => 'b',
+ default => 0
+ },
+ {
name => 'allow_attach_url',
type => 'b',
default => 0
diff --git a/attachment.cgi b/attachment.cgi
index c212c6f36..fbe0bd054 100755
--- a/attachment.cgi
+++ b/attachment.cgi
@@ -49,6 +49,7 @@ use Bugzilla::Util;
use Bugzilla::Bug;
use Bugzilla::Field;
use Bugzilla::Attachment;
+use Bugzilla::Token;
Bugzilla->login();
@@ -103,6 +104,9 @@ elsif ($action eq "update")
Bugzilla->login(LOGIN_REQUIRED);
update();
}
+elsif ($action eq "delete") {
+ delete_attachment();
+}
else
{
ThrowCodeError("unknown_action", { action => $action });
@@ -1329,3 +1333,82 @@ sub update
$template->process("attachment/updated.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
}
+
+# Only administrators can delete attachments.
+sub delete_attachment {
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $dbh = Bugzilla->dbh;
+
+ print $cgi->header();
+
+ $user->in_group('admin')
+ || ThrowUserError('auth_failure', {group => 'admin',
+ action => 'delete',
+ object => 'attachment'});
+
+ Param('allow_attachment_deletion')
+ || ThrowUserError('attachment_deletion_disabled');
+
+ # Make sure the administrator is allowed to edit this attachment.
+ my ($attach_id, $bug_id) = validateID();
+ validateCanEdit($attach_id);
+ validateCanChangeAttachment($attach_id);
+
+ my $attachment = Bugzilla::Attachment->get($attach_id);
+ $attachment->datasize || ThrowUserError('attachment_removed');
+
+ # We don't want to let a malicious URL accidentally delete an attachment.
+ my $token = trim($cgi->param('token'));
+ if ($token) {
+ my ($creator_id, $date, $event) = Bugzilla::Token::GetTokenData($token);
+ unless ($creator_id
+ && ($creator_id == $user->id)
+ && ($event eq "attachment$attach_id"))
+ {
+ # The token is invalid.
+ ThrowUserError('token_inexistent');
+ }
+
+ # The token is valid. Delete the content of the attachment.
+ my $msg;
+ $vars->{'attachid'} = $attach_id;
+ $vars->{'bugid'} = $bug_id;
+ $vars->{'date'} = $date;
+ $vars->{'reason'} = clean_text($cgi->param('reason') || '');
+ $vars->{'mailrecipients'} = { 'changer' => $user->login };
+
+ $template->process("attachment/delete_reason.txt.tmpl", $vars, \$msg)
+ || ThrowTemplateError($template->error());
+
+ $dbh->bz_lock_tables('attachments WRITE', 'attach_data WRITE', 'flags WRITE');
+ $dbh->do('DELETE FROM attach_data WHERE id = ?', undef, $attach_id);
+ $dbh->do('UPDATE attachments SET mimetype = ?, ispatch = ?, isurl = ?
+ WHERE attach_id = ?', undef, ('text/plain', 0, 0, $attach_id));
+ $dbh->do('DELETE FROM flags WHERE attach_id = ?', undef, $attach_id);
+ $dbh->bz_unlock_tables;
+
+ # If the attachment is stored locally, remove it.
+ if (-e $attachment->_get_local_filename) {
+ unlink $attachment->_get_local_filename;
+ }
+
+ # Now delete the token.
+ Bugzilla::Token::DeleteToken($token);
+
+ # Paste the reason provided by the admin into a comment.
+ AppendComment($bug_id, $user->id, $msg);
+
+ $template->process("attachment/updated.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ }
+ else {
+ # Create a token.
+ $token = Bugzilla::Token::IssueSessionToken('attachment' . $attach_id);
+
+ $vars->{'a'} = $attachment;
+ $vars->{'token'} = $token;
+
+ $template->process("attachment/confirm-delete.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ }
+}
diff --git a/template/en/default/admin/params/attachment.html.tmpl b/template/en/default/admin/params/attachment.html.tmpl
index ef89c4af0..785d91822 100644
--- a/template/en/default/admin/params/attachment.html.tmpl
+++ b/template/en/default/admin/params/attachment.html.tmpl
@@ -25,9 +25,13 @@
%]
[% param_descs = {
+ allow_attachment_deletion => "If this option is on, administrators will be able to delete " _
+ "the content of attachments.",
+
allow_attach_url => "If this option is on, it will be possible to " _
"specify a URL when creating an attachment and " _
"treat the URL itself as if it were an attachment.",
+
maxpatchsize => "The maximum size (in kilobytes) of patches. $terms.Bugzilla will not " _
"accept patches greater than this number of kilobytes in size. " _
"To accept patches of any size (subject to the limitations of " _
diff --git a/template/en/default/attachment/confirm-delete.html.tmpl b/template/en/default/attachment/confirm-delete.html.tmpl
new file mode 100644
index 000000000..99007e579
--- /dev/null
+++ b/template/en/default/attachment/confirm-delete.html.tmpl
@@ -0,0 +1,91 @@
+[%# 1.0@bugzilla.org %]
+[%# The contents of this file are subject to the Mozilla Public
+ # License Version 1.1 (the "License"); you may not use this file
+ # except in compliance with the License. You may obtain a copy of
+ # the License at http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS
+ # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ # implied. See the License for the specific language governing
+ # rights and limitations under the License.
+ #
+ # The Original Code is the Bugzilla Bug Tracking System.
+ #
+ # Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
+ #%]
+
+[%# INTERFACE:
+ # a: attachment object; attachment the user wants to delete.
+ # token: string; The token used to identify the session.
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+[% title = BLOCK %]
+ Delete Attachment [% a.id FILTER html %] of
+ [%+ "$terms.Bug " _ a.bug_id FILTER bug_link(a.bug_id) FILTER none %]
+[% END %]
+
+[% PROCESS global/header.html.tmpl title = title %]
+
+<table border="1" cellpadding="4" cellspacing="0">
+ <tr bgcolor="#6666FF">
+ <th valign="top" align="left">Field</th>
+ <th valign="top" align="left">Value</th>
+ </tr>
+ <tr>
+ <td valign="top">Attachment ID:</td>
+ <td valign="top">
+ <a href="attachment.cgi?id=[% a.id FILTER html %]">[% a.id FILTER html %]</a>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">File name:</td>
+ <td valign="top">[% a.filename FILTER html %]</td>
+ </tr>
+ <tr>
+ <td valign="top">Description:</td>
+ <td valign="top">[% a.description FILTER html %]</td>
+ </tr>
+ <tr>
+ <td valign="top">Contained in [% terms.Bug %]:</td>
+ <td valign="top">[% a.bug_id FILTER bug_link(a.bug_id) FILTER none %]</td>
+ </tr>
+ <tr>
+ <td valign="top">Creator:</td>
+ <td valign="top">[% a.attacher.identity FILTER html %]</td>
+ </tr>
+ <tr>
+ <td valign="top">Creation Date:</td>
+ <td valign="top">[% a.attached FILTER time %]</td>
+ </tr>
+</table>
+
+<h2>Confirmation</h2>
+
+<table border="0" cellpadding="20" width="70%" bgcolor="red">
+ <tr>
+ <td>
+ The content of this attachment will be deleted in a <b>irreversible</b> way.
+ </td>
+ </tr>
+</table>
+
+<p>Do you really want to delete this attachment?</p>
+
+<form action="attachment.cgi" method="POST">
+ <label for="reason">Reason of the deletion:</label>
+ <input type="text" id="reason" name="reason" value="" size="80" maxlength="200">
+ <p>
+ <input type="submit" value="Yes, delete">
+ <input type="hidden" name="action" value="delete">
+ <input type="hidden" name="id" value="[% a.id FILTER html %]">
+ <input type="hidden" name="token" value="[% token FILTER html %]">
+</form>
+
+<p>
+ No, cancel this deletion and return to
+ [%+ "$terms.bug " _ a.bug_id FILTER bug_link(a.bug_id) FILTER none %].
+</p>
+
+[% PROCESS global/footer.html.tmpl %]
diff --git a/template/en/default/attachment/delete_reason.txt.tmpl b/template/en/default/attachment/delete_reason.txt.tmpl
new file mode 100644
index 000000000..45879f6ca
--- /dev/null
+++ b/template/en/default/attachment/delete_reason.txt.tmpl
@@ -0,0 +1,33 @@
+[%# 1.0@bugzilla.org %]
+[%# The contents of this file are subject to the Mozilla Public
+ # License Version 1.1 (the "License"); you may not use this file
+ # except in compliance with the License. You may obtain a copy of
+ # the License at http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS
+ # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ # implied. See the License for the specific language governing
+ # rights and limitations under the License.
+ #
+ # The Original Code is the Bugzilla Bug Tracking System.
+ #
+ # Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
+ #%]
+
+[%# INTERFACE:
+ # attachid: ID of the attachment the user wants to delete.
+ # reason: string; The reason provided by the user.
+ # date: the date when the request to delete the attachment was made.
+ #%]
+
+The content of attachment [% attachid %] has been deleted by
+ [%+ user.identity %]
+[% IF reason %]
+who provided the following reason:
+
+[%+ reason %]
+[% ELSE %]
+without providing any reason.
+[% END %]
+
+The token used to delete this attachment was generated at [% date FILTER time %].
diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl
index b2b5b13e5..2f02aae34 100644
--- a/template/en/default/attachment/edit.html.tmpl
+++ b/template/en/default/attachment/edit.html.tmpl
@@ -229,7 +229,12 @@
<b>Filename:</b><br>
<input type="text" size="20" name="filename"
value="[% attachment.filename FILTER html %]"><br>
- <b>Size: </b>[% attachment.datasize FILTER unitconvert %]<br>
+ <b>Size:</b>
+ [% IF attachment.datasize %]
+ [%+ attachment.datasize FILTER unitconvert %]
+ [% ELSE %]
+ <em>deleted</em>
+ [% END %]<br>
<b>MIME Type:</b><br>
<input type="text" size="20" name="contenttypeentry"
@@ -269,10 +274,17 @@
[% IF attachment.ispatch && patchviewerinstalled %]
| <a href="attachment.cgi?id=[% attachment.id %]&action=diff">Diff</a>
[% END %]
+ [% IF Param("allow_attachment_deletion")
+ && user.groups.admin
+ && attachment.datasize > 0 %]
+ | <a href="attachment.cgi?id=[% attachment.id %]&action=delete">Delete</a>
+ [% END %]
</small>
</td>
- [% IF isviewable %]
+ [% IF !attachment.datasize %]
+ <td width="75%"><b>The content of this attachment has been deleted.</b></td>
+ [% ELSIF isviewable %]
<td width="75%">
[% INCLUDE global/textarea.html.tmpl
id = 'editFrame'
diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl
index 03368ce26..0b42c0c3c 100644
--- a/template/en/default/attachment/list.html.tmpl
+++ b/template/en/default/attachment/list.html.tmpl
@@ -56,7 +56,13 @@
</a>
</td>
<td valign="top">[% attachment.attached FILTER time %]</td>
- <td valign="top">[% attachment.datasize FILTER unitconvert %]</td>
+ <td valign="top">
+ [% IF attachment.datasize %]
+ [% attachment.datasize FILTER unitconvert %]
+ [% ELSE %]
+ <em>deleted</em>
+ [% END %]
+ </td>
[% IF show_attachment_flags %]
<td valign="top">
diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl
index 54aa31ee0..c50955db8 100644
--- a/template/en/default/global/user-error.html.tmpl
+++ b/template/en/default/global/user-error.html.tmpl
@@ -194,6 +194,10 @@
versions
[% END %].
+ [% ELSIF error == "attachment_deletion_disabled" %]
+ [% title = "Attachment Deletion Disabled" %]
+ Attachment deletion is disabled on this installation.
+
[% ELSIF error == "attachment_removed" %]
[% title = "Attachment Removed" %]
The attachment you are attempting to access has been removed.