diff options
author | 2019-01-30 20:00:43 -0500 | |
---|---|---|
committer | 2019-01-30 20:31:44 -0500 | |
commit | 7f3a749d7bd78a3e4aee163f562d7e95b0954b44 (patch) | |
tree | f86271c0b1f3ece6d55d8fa44767d41bb890f1f6 /attachment.cgi | |
parent | Bug 1226123 - Email addresses with an apostrophe in them break the "Send Mail... (diff) | |
download | bugzilla-7f3a749d7bd78a3e4aee163f562d7e95b0954b44.tar.gz bugzilla-7f3a749d7bd78a3e4aee163f562d7e95b0954b44.tar.bz2 bugzilla-7f3a749d7bd78a3e4aee163f562d7e95b0954b44.zip |
no bug - reformat all the code using the new perltidy rules
Diffstat (limited to 'attachment.cgi')
-rwxr-xr-x | attachment.cgi | 1190 |
1 files changed, 614 insertions, 576 deletions
diff --git a/attachment.cgi b/attachment.cgi index 4cd9229fb..d8244b080 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -16,8 +16,8 @@ use Bugzilla; use Bugzilla::BugMail; use Bugzilla::Constants; use Bugzilla::Error; -use Bugzilla::Flag; -use Bugzilla::FlagType; +use Bugzilla::Flag; +use Bugzilla::FlagType; use Bugzilla::User; use Bugzilla::Util; use Bugzilla::Bug; @@ -26,15 +26,15 @@ use Bugzilla::Attachment::PatchReader; use Bugzilla::Token; use Encode qw(encode find_encoding); -use Encode::MIME::Header; # Required to alter Encode::Encoding{'MIME-Q'}. +use Encode::MIME::Header; # Required to alter Encode::Encoding{'MIME-Q'}. # For most scripts we don't make $cgi and $template global variables. But # when preparing Bugzilla for mod_perl, this script used these # variables in so many subroutines that it was easier to just # make them globals. -local our $cgi = Bugzilla->cgi; -local our $template = Bugzilla->template; -local our $vars = {}; +local our $cgi = Bugzilla->cgi; +local our $template = Bugzilla->template; +local our $vars = {}; local $Bugzilla::CGI::ALLOW_UNSAFE_RESPONSE = 1; # All calls to this script should contain an "action" variable whose @@ -49,57 +49,48 @@ my $format = $cgi->param('format') || ''; # You must use the appropriate urlbase/sslbase param when doing anything # but viewing an attachment, or a raw diff. if ($action ne 'view' - && (($action !~ /^(?:interdiff|diff)$/) || $format ne 'raw')) + && (($action !~ /^(?:interdiff|diff)$/) || $format ne 'raw')) { - do_ssl_redirect_if_required(); - if ($cgi->url_is_attachment_base) { - $cgi->redirect_to_urlbase; - } - Bugzilla->login(); + do_ssl_redirect_if_required(); + if ($cgi->url_is_attachment_base) { + $cgi->redirect_to_urlbase; + } + Bugzilla->login(); } # When viewing an attachment, do not request credentials if we are on # the alternate host. Let view() decide when to call Bugzilla->login. -if ($action eq "view") -{ - view(); +if ($action eq "view") { + view(); } -elsif ($action eq "interdiff") -{ - interdiff(); +elsif ($action eq "interdiff") { + interdiff(); } -elsif ($action eq "diff") -{ - diff(); +elsif ($action eq "diff") { + diff(); } -elsif ($action eq "viewall") -{ - viewall(); +elsif ($action eq "viewall") { + viewall(); } -elsif ($action eq "enter") -{ - Bugzilla->login(LOGIN_REQUIRED); - enter(); +elsif ($action eq "enter") { + Bugzilla->login(LOGIN_REQUIRED); + enter(); } -elsif ($action eq "insert") -{ - Bugzilla->login(LOGIN_REQUIRED); - insert(); +elsif ($action eq "insert") { + Bugzilla->login(LOGIN_REQUIRED); + insert(); } -elsif ($action eq "edit") -{ - edit(); +elsif ($action eq "edit") { + edit(); } -elsif ($action eq "update") -{ - Bugzilla->login(LOGIN_REQUIRED); - update(); +elsif ($action eq "update") { + Bugzilla->login(LOGIN_REQUIRED); + update(); } elsif ($action eq "delete") { - delete_attachment(); + delete_attachment(); } -else -{ +else { ThrowUserError('unknown_action', {action => $action}); } @@ -121,72 +112,73 @@ exit; # Returns an attachment object. sub validateID { - my($param, $dont_validate_access) = @_; - $param ||= 'id'; + my ($param, $dont_validate_access) = @_; + $param ||= 'id'; - # If we're not doing interdiffs, check if id wasn't specified and - # prompt them with a page that allows them to choose an attachment. - # Happens when calling plain attachment.cgi from the urlbar directly - if ($param eq 'id' && !$cgi->param('id')) { - print $cgi->header(); - $template->process("attachment/choose.html.tmpl", $vars) || - ThrowTemplateError($template->error()); - exit; - } - - my $attach_id = $cgi->param($param); - - # Validate the specified attachment id. detaint kills $attach_id if - # non-natural, so use the original value from $cgi in our exception - # message here. - detaint_natural($attach_id) - || ThrowUserError("invalid_attach_id", - { attach_id => scalar $cgi->param($param) }); - - # Make sure the attachment exists in the database. - my $attachment = new Bugzilla::Attachment({ id => $attach_id, cache => 1 }) - || ThrowUserError("invalid_attach_id", { attach_id => $attach_id }); - - return $attachment if ($dont_validate_access || check_can_access($attachment)); + # If we're not doing interdiffs, check if id wasn't specified and + # prompt them with a page that allows them to choose an attachment. + # Happens when calling plain attachment.cgi from the urlbar directly + if ($param eq 'id' && !$cgi->param('id')) { + print $cgi->header(); + $template->process("attachment/choose.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } + + my $attach_id = $cgi->param($param); + + # Validate the specified attachment id. detaint kills $attach_id if + # non-natural, so use the original value from $cgi in our exception + # message here. + detaint_natural($attach_id) + || ThrowUserError("invalid_attach_id", + {attach_id => scalar $cgi->param($param)}); + + # Make sure the attachment exists in the database. + my $attachment = new Bugzilla::Attachment({id => $attach_id, cache => 1}) + || ThrowUserError("invalid_attach_id", {attach_id => $attach_id}); + + return $attachment if ($dont_validate_access || check_can_access($attachment)); } # Make sure the current user has access to the specified attachment. sub check_can_access { - my $attachment = shift; - my $user = Bugzilla->user; - - # Make sure the user is authorized to access this attachment's bug. - Bugzilla::Bug->check({ id => $attachment->bug_id, cache => 1 }); - if ($attachment->isprivate && $user->id != $attachment->attacher->id - && !$user->is_insider) - { - ThrowUserError('auth_failure', {action => 'access', - object => 'attachment', - attach_id => $attachment->id}); - } - return 1; + my $attachment = shift; + my $user = Bugzilla->user; + + # Make sure the user is authorized to access this attachment's bug. + Bugzilla::Bug->check({id => $attachment->bug_id, cache => 1}); + if ( $attachment->isprivate + && $user->id != $attachment->attacher->id + && !$user->is_insider) + { + ThrowUserError('auth_failure', + {action => 'access', object => 'attachment', attach_id => $attachment->id}); + } + return 1; } # Determines if the attachment is public -- that is, if users who are # not logged in have access to the attachment sub attachmentIsPublic { - my $attachment = shift; + my $attachment = shift; - return 0 if Bugzilla->params->{'requirelogin'}; - return 0 if $attachment->isprivate; + return 0 if Bugzilla->params->{'requirelogin'}; + return 0 if $attachment->isprivate; - my $anon_user = new Bugzilla::User; - return $anon_user->can_see_bug($attachment->bug_id); + my $anon_user = new Bugzilla::User; + return $anon_user->can_see_bug($attachment->bug_id); } # Validates format of a diff/interdiff. Takes a list as an parameter, which # defines the valid format values. Will throw an error if the format is not # in the list. Returns either the user selected or default format. sub validateFormat { + # receives a list of legal formats; first item is a default my $format = $cgi->param('format') || $_[0]; if (not grep($_ eq $format, @_)) { - ThrowUserError("invalid_format", { format => $format, formats => \@_ }); + ThrowUserError("invalid_format", {format => $format, formats => \@_}); } return $format; @@ -195,125 +187,139 @@ sub validateFormat { # Gets the attachment object(s) generated by validateID, while ensuring # attachbase and token authentication is used when required. sub get_attachment { - my @field_names = @_ ? @_ : qw(id); - - my %attachments; - - if (use_attachbase()) { - # Load each attachment, and ensure they are all from the same bug - my $bug_id = 0; + my @field_names = @_ ? @_ : qw(id); + + my %attachments; + + if (use_attachbase()) { + + # Load each attachment, and ensure they are all from the same bug + my $bug_id = 0; + foreach my $field_name (@field_names) { + my $attachment = validateID($field_name, 1); + if (!$bug_id) { + $bug_id = $attachment->bug_id; + } + elsif ($attachment->bug_id != $bug_id) { + ThrowUserError('attachment_bug_id_mismatch'); + } + $attachments{$field_name} = $attachment; + } + my @args = map { $_ . '=' . $attachments{$_}->id } @field_names; + my $cgi_params = $cgi->canonicalise_query(@field_names, 't', 'Bugzilla_login', + 'Bugzilla_password'); + push(@args, $cgi_params) if $cgi_params; + my $path = 'attachment.cgi?' . join('&', @args); + + # Make sure the attachment is served from the correct server. + if ($cgi->url_is_attachment_base($bug_id)) { + + # No need to validate the token for public attachments. We cannot request + # credentials as we are on the alternate host. + if (!all_attachments_are_public(\%attachments)) { + my $token = $cgi->param('t'); + my ($userid, undef, $token_data) = Bugzilla::Token::GetTokenData($token); + my %token_data = unpack_token_data($token_data); + my $valid_token = 1; foreach my $field_name (@field_names) { - my $attachment = validateID($field_name, 1); - if (!$bug_id) { - $bug_id = $attachment->bug_id; - } elsif ($attachment->bug_id != $bug_id) { - ThrowUserError('attachment_bug_id_mismatch'); - } - $attachments{$field_name} = $attachment; - } - my @args = map { $_ . '=' . $attachments{$_}->id } @field_names; - my $cgi_params = $cgi->canonicalise_query(@field_names, 't', - 'Bugzilla_login', 'Bugzilla_password'); - push(@args, $cgi_params) if $cgi_params; - my $path = 'attachment.cgi?' . join('&', @args); - - # Make sure the attachment is served from the correct server. - if ($cgi->url_is_attachment_base($bug_id)) { - # No need to validate the token for public attachments. We cannot request - # credentials as we are on the alternate host. - if (!all_attachments_are_public(\%attachments)) { - my $token = $cgi->param('t'); - my ($userid, undef, $token_data) = Bugzilla::Token::GetTokenData($token); - my %token_data = unpack_token_data($token_data); - my $valid_token = 1; - foreach my $field_name (@field_names) { - my $token_id = $token_data{$field_name}; - if (!$token_id - || !detaint_natural($token_id) - || $attachments{$field_name}->id != $token_id) - { - $valid_token = 0; - last; - } - } - unless ($userid && $valid_token) { - # Not a valid token. - print $cgi->redirect('-location' => correct_urlbase() . $path); - exit; - } - # Change current user without creating cookies. - Bugzilla->set_user(new Bugzilla::User($userid)); - # Tokens are single use only, delete it. - delete_token($token); - } + my $token_id = $token_data{$field_name}; + if ( !$token_id + || !detaint_natural($token_id) + || $attachments{$field_name}->id != $token_id) + { + $valid_token = 0; + last; + } } - elsif ($cgi->url_is_attachment_base) { - # If we come here, this means that each bug has its own host - # for attachments, and that we are trying to view one attachment - # using another bug's host. That's not desired. - $cgi->redirect_to_urlbase; - } - else { - # We couldn't call Bugzilla->login earlier as we first had to - # make sure we were not going to request credentials on the - # alternate host. - Bugzilla->login(); - my $attachbase = Bugzilla->params->{'attachment_base'}; - # Replace %bugid% by the ID of the bug the attachment - # belongs to, if present. - $attachbase =~ s/\%bugid\%/$bug_id/; - if (all_attachments_are_public(\%attachments)) { - # No need for a token; redirect to attachment base. - print $cgi->redirect(-location => $attachbase . $path); - exit; - } else { - # Make sure the user can view the attachment. - foreach my $field_name (@field_names) { - check_can_access($attachments{$field_name}); - } - # Create a token and redirect. - my $token = url_quote(issue_session_token(pack_token_data(\%attachments))); - print $cgi->redirect(-location => $attachbase . "$path&t=$token"); - exit; - } + unless ($userid && $valid_token) { + + # Not a valid token. + print $cgi->redirect('-location' => correct_urlbase() . $path); + exit; } - } else { - do_ssl_redirect_if_required(); - # No alternate host is used. Request credentials if required. - Bugzilla->login(); + + # Change current user without creating cookies. + Bugzilla->set_user(new Bugzilla::User($userid)); + + # Tokens are single use only, delete it. + delete_token($token); + } + } + elsif ($cgi->url_is_attachment_base) { + + # If we come here, this means that each bug has its own host + # for attachments, and that we are trying to view one attachment + # using another bug's host. That's not desired. + $cgi->redirect_to_urlbase; + } + else { + # We couldn't call Bugzilla->login earlier as we first had to + # make sure we were not going to request credentials on the + # alternate host. + Bugzilla->login(); + my $attachbase = Bugzilla->params->{'attachment_base'}; + + # Replace %bugid% by the ID of the bug the attachment + # belongs to, if present. + $attachbase =~ s/\%bugid\%/$bug_id/; + if (all_attachments_are_public(\%attachments)) { + + # No need for a token; redirect to attachment base. + print $cgi->redirect(-location => $attachbase . $path); + exit; + } + else { + # Make sure the user can view the attachment. foreach my $field_name (@field_names) { - $attachments{$field_name} = validateID($field_name); + check_can_access($attachments{$field_name}); } + + # Create a token and redirect. + my $token = url_quote(issue_session_token(pack_token_data(\%attachments))); + print $cgi->redirect(-location => $attachbase . "$path&t=$token"); + exit; + } + } + } + else { + do_ssl_redirect_if_required(); + + # No alternate host is used. Request credentials if required. + Bugzilla->login(); + foreach my $field_name (@field_names) { + $attachments{$field_name} = validateID($field_name); } + } - return wantarray - ? map { $attachments{$_} } @field_names - : $attachments{$field_names[0]}; + return + wantarray + ? map { $attachments{$_} } @field_names + : $attachments{$field_names[0]}; } sub all_attachments_are_public { - my $attachments = shift; - foreach my $field_name (keys %$attachments) { - if (!attachmentIsPublic($attachments->{$field_name})) { - return 0; - } + my $attachments = shift; + foreach my $field_name (keys %$attachments) { + if (!attachmentIsPublic($attachments->{$field_name})) { + return 0; } - return 1; + } + return 1; } sub pack_token_data { - my $attachments = shift; - return join(' ', map { $_ . '=' . $attachments->{$_}->id } keys %$attachments); + my $attachments = shift; + return join(' ', map { $_ . '=' . $attachments->{$_}->id } keys %$attachments); } sub unpack_token_data { - my @token_data = split(/ /, shift || ''); - my %data; - foreach my $token (@token_data) { - my ($field_name, $attach_id) = split('=', $token); - $data{$field_name} = $attach_id; - } - return %data; + my @token_data = split(/ /, shift || ''); + my %data; + foreach my $token (@token_data) { + my ($field_name, $attach_id) = split('=', $token); + $data{$field_name} = $attach_id; + } + return %data; } ################################################################################ @@ -322,262 +328,284 @@ sub unpack_token_data { # Display an attachment. sub view { - my $attachment = get_attachment(); + my $attachment = get_attachment(); - # At this point, Bugzilla->login has been called if it had to. - my $contenttype = $attachment->contenttype; - my $filename = $attachment->filename; + # At this point, Bugzilla->login has been called if it had to. + my $contenttype = $attachment->contenttype; + my $filename = $attachment->filename; - # Bug 111522: allow overriding content-type manually in the posted form - # params. - if (defined $cgi->param('content_type')) { - $contenttype = $attachment->_check_content_type($cgi->param('content_type')); - } + # Bug 111522: allow overriding content-type manually in the posted form + # params. + if (defined $cgi->param('content_type')) { + $contenttype = $attachment->_check_content_type($cgi->param('content_type')); + } - # Return the appropriate HTTP response headers. - $attachment->datasize || ThrowUserError("attachment_removed"); - - $filename =~ s/^.*[\/\\]//; - # escape quotes and backslashes in the filename, per RFCs 2045/822 - $filename =~ s/\\/\\\\/g; # escape backslashes - $filename =~ s/"/\\"/g; # escape quotes - - # Avoid line wrapping done by Encode, which we don't need for HTTP - # headers. See discussion in bug 328628 for details. - local $Encode::Encoding{'MIME-Q'}->{'bpl'} = 10000; - $filename = encode('MIME-Q', $filename); - - my $disposition = Bugzilla->params->{'allow_attachment_display'} ? 'inline' : 'attachment'; - - # Don't send a charset header with attachments--they might not be UTF-8. - # However, we do allow people to explicitly specify a charset if they - # want. - if ($contenttype !~ /\bcharset=/i) { - # In order to prevent Apache from adding a charset, we have to send a - # charset that's a single space. - $cgi->charset(' '); - if (Bugzilla->feature('detect_charset') && $contenttype =~ /^text\//) { - my $encoding = detect_encoding($attachment->data); - if ($encoding) { - $cgi->charset(find_encoding($encoding)->mime_name); - } - } + # Return the appropriate HTTP response headers. + $attachment->datasize || ThrowUserError("attachment_removed"); + + $filename =~ s/^.*[\/\\]//; + + # escape quotes and backslashes in the filename, per RFCs 2045/822 + $filename =~ s/\\/\\\\/g; # escape backslashes + $filename =~ s/"/\\"/g; # escape quotes + + # Avoid line wrapping done by Encode, which we don't need for HTTP + # headers. See discussion in bug 328628 for details. + local $Encode::Encoding{'MIME-Q'}->{'bpl'} = 10000; + $filename = encode('MIME-Q', $filename); + + my $disposition + = Bugzilla->params->{'allow_attachment_display'} ? 'inline' : 'attachment'; + + # Don't send a charset header with attachments--they might not be UTF-8. + # However, we do allow people to explicitly specify a charset if they + # want. + if ($contenttype !~ /\bcharset=/i) { + + # In order to prevent Apache from adding a charset, we have to send a + # charset that's a single space. + $cgi->charset(' '); + if (Bugzilla->feature('detect_charset') && $contenttype =~ /^text\//) { + my $encoding = detect_encoding($attachment->data); + if ($encoding) { + $cgi->charset(find_encoding($encoding)->mime_name); + } } - print $cgi->header(-type=>"$contenttype; name=\"$filename\"", - -content_disposition=> "$disposition; filename=\"$filename\"", - -content_length => $attachment->datasize); - disable_utf8(); - print $attachment->data; + } + print $cgi->header( + -type => "$contenttype; name=\"$filename\"", + -content_disposition => "$disposition; filename=\"$filename\"", + -content_length => $attachment->datasize + ); + disable_utf8(); + print $attachment->data; } sub interdiff { - # Retrieve and validate parameters - my $format = validateFormat('html', 'raw'); - my($old_attachment, $new_attachment); - if ($format eq 'raw') { - ($old_attachment, $new_attachment) = get_attachment('oldid', 'newid'); - } else { - $old_attachment = validateID('oldid'); - $new_attachment = validateID('newid'); - } - Bugzilla::Attachment::PatchReader::process_interdiff( - $old_attachment, $new_attachment, $format); + # Retrieve and validate parameters + my $format = validateFormat('html', 'raw'); + my ($old_attachment, $new_attachment); + if ($format eq 'raw') { + ($old_attachment, $new_attachment) = get_attachment('oldid', 'newid'); + } + else { + $old_attachment = validateID('oldid'); + $new_attachment = validateID('newid'); + } + + Bugzilla::Attachment::PatchReader::process_interdiff($old_attachment, + $new_attachment, $format); } sub diff { - # Retrieve and validate parameters - my $format = validateFormat('html', 'raw'); - my $attachment = $format eq 'raw' ? get_attachment() : validateID(); - - # If it is not a patch, view normally. - if (!$attachment->ispatch) { - view(); - return; - } - Bugzilla::Attachment::PatchReader::process_diff($attachment, $format); + # Retrieve and validate parameters + my $format = validateFormat('html', 'raw'); + my $attachment = $format eq 'raw' ? get_attachment() : validateID(); + + # If it is not a patch, view normally. + if (!$attachment->ispatch) { + view(); + return; + } + + Bugzilla::Attachment::PatchReader::process_diff($attachment, $format); } # Display all attachments for a given bug in a series of IFRAMEs within one # HTML page. sub viewall { - # Retrieve and validate parameters - my $bug = Bugzilla::Bug->check({ id => scalar $cgi->param('bugid'), cache => 1 }); - my $attachments = Bugzilla::Attachment->get_attachments_by_bug($bug); - # Ignore deleted attachments. - @$attachments = grep { $_->datasize } @$attachments; + # Retrieve and validate parameters + my $bug = Bugzilla::Bug->check({id => scalar $cgi->param('bugid'), cache => 1}); - if ($cgi->param('hide_obsolete')) { - @$attachments = grep { !$_->isobsolete } @$attachments; - $vars->{'hide_obsolete'} = 1; - } + my $attachments = Bugzilla::Attachment->get_attachments_by_bug($bug); - # Define the variables and functions that will be passed to the UI template. - $vars->{'bug'} = $bug; - $vars->{'attachments'} = $attachments; + # Ignore deleted attachments. + @$attachments = grep { $_->datasize } @$attachments; - print $cgi->header(); + if ($cgi->param('hide_obsolete')) { + @$attachments = grep { !$_->isobsolete } @$attachments; + $vars->{'hide_obsolete'} = 1; + } - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/show-multiple.html.tmpl", $vars) - || ThrowTemplateError($template->error()); + # Define the variables and functions that will be passed to the UI template. + $vars->{'bug'} = $bug; + $vars->{'attachments'} = $attachments; + + print $cgi->header(); + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/show-multiple.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Display a form for entering a new attachment. sub enter { - # Retrieve and validate parameters - my $bug = Bugzilla::Bug->check(scalar $cgi->param('bugid')); - my $bugid = $bug->id; - Bugzilla::Attachment->_check_bug($bug); - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - - # Retrieve the attachments the user can edit from the database and write - # them into an array of hashes where each hash represents one attachment. - - my ($can_edit, $not_private) = ('', ''); - if (!$user->in_group('editbugs', $bug->product_id)) { - $can_edit = "AND submitter_id = " . $user->id; - } - if (!$user->is_insider) { - $not_private = "AND isprivate = 0"; - } - my $attach_ids = $dbh->selectcol_arrayref( - "SELECT attach_id + + # Retrieve and validate parameters + my $bug = Bugzilla::Bug->check(scalar $cgi->param('bugid')); + my $bugid = $bug->id; + Bugzilla::Attachment->_check_bug($bug); + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + # Retrieve the attachments the user can edit from the database and write + # them into an array of hashes where each hash represents one attachment. + + my ($can_edit, $not_private) = ('', ''); + if (!$user->in_group('editbugs', $bug->product_id)) { + $can_edit = "AND submitter_id = " . $user->id; + } + if (!$user->is_insider) { + $not_private = "AND isprivate = 0"; + } + my $attach_ids = $dbh->selectcol_arrayref( + "SELECT attach_id FROM attachments WHERE bug_id = ? AND isobsolete = 0 $can_edit $not_private - ORDER BY attach_id", - undef, $bugid); - - # Define the variables and functions that will be passed to the UI template. - $vars->{'bug'} = $bug; - $vars->{'attachments'} = Bugzilla::Attachment->new_from_list($attach_ids); - - my $flag_types = Bugzilla::FlagType::match({ - 'target_type' => 'attachment', - 'product_id' => $bug->product_id, - 'component_id' => $bug->component_id - }); - $vars->{'flag_types'} = $flag_types; - $vars->{'any_flags_requesteeble'} = - grep { $_->is_requestable && $_->is_requesteeble } @$flag_types; - $vars->{'token'} = issue_session_token('create_attachment'); - - print $cgi->header(); - - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/create.html.tmpl", $vars) - || ThrowTemplateError($template->error()); + ORDER BY attach_id", undef, $bugid + ); + + # Define the variables and functions that will be passed to the UI template. + $vars->{'bug'} = $bug; + $vars->{'attachments'} = Bugzilla::Attachment->new_from_list($attach_ids); + + my $flag_types = Bugzilla::FlagType::match({ + 'target_type' => 'attachment', + 'product_id' => $bug->product_id, + 'component_id' => $bug->component_id + }); + $vars->{'flag_types'} = $flag_types; + $vars->{'any_flags_requesteeble'} + = grep { $_->is_requestable && $_->is_requesteeble } @$flag_types; + $vars->{'token'} = issue_session_token('create_attachment'); + + print $cgi->header(); + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/create.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Insert a new attachment into the database. sub insert { - my $dbh = Bugzilla->dbh; - my $user = Bugzilla->user; - - $dbh->bz_start_transaction; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + $dbh->bz_start_transaction; + + # Retrieve and validate parameters + my $bug = Bugzilla::Bug->check(scalar $cgi->param('bugid')); + my $bugid = $bug->id; + my ($timestamp) = $dbh->selectrow_array("SELECT NOW()"); + + # Detect if the user already used the same form to submit an attachment + my $token = trim($cgi->param('token')); + check_token_data($token, 'create_attachment', 'index.cgi'); + + # Check attachments the user tries to mark as obsolete. + my @obsolete_attachments; + if ($cgi->param('obsolete')) { + my @obsolete = $cgi->param('obsolete'); + @obsolete_attachments + = Bugzilla::Attachment->validate_obsolete($bug, \@obsolete); + } - # Retrieve and validate parameters - my $bug = Bugzilla::Bug->check(scalar $cgi->param('bugid')); - my $bugid = $bug->id; - my ($timestamp) = $dbh->selectrow_array("SELECT NOW()"); + # Must be called before create() as it may alter $cgi->param('ispatch'). + my $content_type = Bugzilla::Attachment::get_content_type(); + + # Get the filehandle of the attachment. + my $data_fh = $cgi->upload('data'); + my $attach_text = $cgi->param('attach_text'); + + my $attachment = Bugzilla::Attachment->create({ + bug => $bug, + creation_ts => $timestamp, + data => $attach_text || $data_fh, + description => scalar $cgi->param('description'), + filename => $attach_text ? "file_$bugid.txt" : $data_fh, + ispatch => scalar $cgi->param('ispatch'), + isprivate => scalar $cgi->param('isprivate'), + mimetype => $content_type, + }); + + # Delete the token used to create this attachment. + delete_token($token); + + foreach my $obsolete_attachment (@obsolete_attachments) { + $obsolete_attachment->set_is_obsolete(1); + $obsolete_attachment->update($timestamp); + } - # Detect if the user already used the same form to submit an attachment - my $token = trim($cgi->param('token')); - check_token_data($token, 'create_attachment', 'index.cgi'); + my ($flags, $new_flags) + = Bugzilla::Flag->extract_flags_from_cgi($bug, $attachment, $vars, + SKIP_REQUESTEE_ON_ERROR); + $attachment->set_flags($flags, $new_flags); - # Check attachments the user tries to mark as obsolete. - my @obsolete_attachments; - if ($cgi->param('obsolete')) { - my @obsolete = $cgi->param('obsolete'); - @obsolete_attachments = Bugzilla::Attachment->validate_obsolete($bug, \@obsolete); + # Insert a comment about the new attachment into the database. + my $comment = $cgi->param('comment'); + $comment = '' unless defined $comment; + $bug->add_comment( + $comment, + { + isprivate => $attachment->isprivate, + type => CMT_ATTACHMENT_CREATED, + extra_data => $attachment->id } + ); - # Must be called before create() as it may alter $cgi->param('ispatch'). - my $content_type = Bugzilla::Attachment::get_content_type(); - - # Get the filehandle of the attachment. - my $data_fh = $cgi->upload('data'); - my $attach_text = $cgi->param('attach_text'); - - my $attachment = Bugzilla::Attachment->create( - {bug => $bug, - creation_ts => $timestamp, - data => $attach_text || $data_fh, - description => scalar $cgi->param('description'), - filename => $attach_text ? "file_$bugid.txt" : $data_fh, - ispatch => scalar $cgi->param('ispatch'), - isprivate => scalar $cgi->param('isprivate'), - mimetype => $content_type, - }); - - # Delete the token used to create this attachment. - delete_token($token); + # Assign the bug to the user, if they are allowed to take it + my $owner = ""; + if ($cgi->param('takebug') && $user->in_group('editbugs', $bug->product_id)) { - foreach my $obsolete_attachment (@obsolete_attachments) { - $obsolete_attachment->set_is_obsolete(1); - $obsolete_attachment->update($timestamp); + # When taking a bug, we have to follow the workflow. + my $bug_status = $cgi->param('bug_status') || ''; + ($bug_status) = grep { $_->name eq $bug_status } @{$bug->status->can_change_to}; + + if ( $bug_status + && $bug_status->is_open + && ($bug_status->name ne 'UNCONFIRMED' || $bug->product_obj->allows_unconfirmed) + ) + { + $bug->set_bug_status($bug_status->name); + $bug->clear_resolution(); } - my ($flags, $new_flags) = Bugzilla::Flag->extract_flags_from_cgi( - $bug, $attachment, $vars, SKIP_REQUESTEE_ON_ERROR); - $attachment->set_flags($flags, $new_flags); + # Make sure the person we are taking the bug from gets mail. + $owner = $bug->assigned_to->login; + $bug->set_assigned_to($user); + } - # Insert a comment about the new attachment into the database. - my $comment = $cgi->param('comment'); - $comment = '' unless defined $comment; - $bug->add_comment($comment, { isprivate => $attachment->isprivate, - type => CMT_ATTACHMENT_CREATED, - extra_data => $attachment->id }); - - # Assign the bug to the user, if they are allowed to take it - my $owner = ""; - if ($cgi->param('takebug') && $user->in_group('editbugs', $bug->product_id)) { - # When taking a bug, we have to follow the workflow. - my $bug_status = $cgi->param('bug_status') || ''; - ($bug_status) = grep { $_->name eq $bug_status } - @{ $bug->status->can_change_to }; - - if ($bug_status && $bug_status->is_open - && ($bug_status->name ne 'UNCONFIRMED' - || $bug->product_obj->allows_unconfirmed)) - { - $bug->set_bug_status($bug_status->name); - $bug->clear_resolution(); - } - # Make sure the person we are taking the bug from gets mail. - $owner = $bug->assigned_to->login; - $bug->set_assigned_to($user); - } + $bug->add_cc($user) if $cgi->param('addselfcc'); + $bug->update($timestamp); - $bug->add_cc($user) if $cgi->param('addselfcc'); - $bug->update($timestamp); + # We have to update the attachment after updating the bug, to ensure new + # comments are available. + $attachment->update($timestamp); - # We have to update the attachment after updating the bug, to ensure new - # comments are available. - $attachment->update($timestamp); + $dbh->bz_commit_transaction; - $dbh->bz_commit_transaction; + # Define the variables and functions that will be passed to the UI template. + $vars->{'attachment'} = $attachment; - # Define the variables and functions that will be passed to the UI template. - $vars->{'attachment'} = $attachment; - # We cannot reuse the $bug object as delta_ts has eventually been updated - # since the object was created. - $vars->{'bugs'} = [new Bugzilla::Bug($bugid)]; - $vars->{'header_done'} = 1; - $vars->{'contenttypemethod'} = $cgi->param('contenttypemethod'); + # We cannot reuse the $bug object as delta_ts has eventually been updated + # since the object was created. + $vars->{'bugs'} = [new Bugzilla::Bug($bugid)]; + $vars->{'header_done'} = 1; + $vars->{'contenttypemethod'} = $cgi->param('contenttypemethod'); - my $recipients = { 'changer' => $user, 'owner' => $owner }; - $vars->{'sent_bugmail'} = Bugzilla::BugMail::Send($bugid, $recipients); + my $recipients = {'changer' => $user, 'owner' => $owner}; + $vars->{'sent_bugmail'} = Bugzilla::BugMail::Send($bugid, $recipients); - print $cgi->header(); - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/created.html.tmpl", $vars) - || ThrowTemplateError($template->error()); + print $cgi->header(); + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/created.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Displays a form for editing attachment properties. @@ -585,227 +613,237 @@ sub insert { # is private and the user does not belong to the insider group. # Validations are done later when the user submits changes. sub edit { - my $attachment = validateID(); + my $attachment = validateID(); - my $bugattachments = - Bugzilla::Attachment->get_attachments_by_bug($attachment->bug); + my $bugattachments + = Bugzilla::Attachment->get_attachments_by_bug($attachment->bug); - my $any_flags_requesteeble = grep { $_->is_requestable && $_->is_requesteeble } - @{ $attachment->flag_types }; - # Useful in case a flagtype is no longer requestable but a requestee - # has been set before we turned off that bit. - $any_flags_requesteeble ||= grep { $_->requestee_id } @{ $attachment->flags }; - $vars->{'any_flags_requesteeble'} = $any_flags_requesteeble; - $vars->{'attachment'} = $attachment; - $vars->{'attachments'} = $bugattachments; + my $any_flags_requesteeble = grep { $_->is_requestable && $_->is_requesteeble } + @{$attachment->flag_types}; - print $cgi->header(); + # Useful in case a flagtype is no longer requestable but a requestee + # has been set before we turned off that bit. + $any_flags_requesteeble ||= grep { $_->requestee_id } @{$attachment->flags}; + $vars->{'any_flags_requesteeble'} = $any_flags_requesteeble; + $vars->{'attachment'} = $attachment; + $vars->{'attachments'} = $bugattachments; - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/edit.html.tmpl", $vars) - || ThrowTemplateError($template->error()); + print $cgi->header(); + + # Generate and return the UI (HTML page) from the appropriate template. + $template->process("attachment/edit.html.tmpl", $vars) + || ThrowTemplateError($template->error()); } # Updates an attachment record. Only users with "editbugs" privileges, # (or the original attachment's submitter) can edit the attachment. # Users cannot edit the content of the attachment itself. sub update { - my $user = Bugzilla->user; - my $dbh = Bugzilla->dbh; - - # Start a transaction in preparation for updating the attachment. - $dbh->bz_start_transaction(); - - # Retrieve and validate parameters - my $attachment = validateID(); - my $bug = $attachment->bug; - $attachment->_check_bug; - my $can_edit = $attachment->validate_can_edit; - - if ($can_edit) { - $attachment->set_description(scalar $cgi->param('description')); - $attachment->set_is_patch(scalar $cgi->param('ispatch')); - $attachment->set_content_type(scalar $cgi->param('contenttypeentry')); - $attachment->set_is_obsolete(scalar $cgi->param('isobsolete')); - $attachment->set_is_private(scalar $cgi->param('isprivate')); - $attachment->set_filename(scalar $cgi->param('filename')); - - # Now make sure the attachment has not been edited since we loaded the page. - my $delta_ts = $cgi->param('delta_ts'); - my $modification_time = $attachment->modification_time; - - if ($delta_ts && $delta_ts ne $modification_time) { - datetime_from($delta_ts) - or ThrowCodeError('invalid_timestamp', { timestamp => $delta_ts }); - ($vars->{'operations'}) = $bug->get_activity($attachment->id, $delta_ts); - - # If the modification date changed but there is no entry in - # the activity table, this means someone commented only. - # In this case, there is no reason to midair. - if (scalar(@{$vars->{'operations'}})) { - $cgi->param('delta_ts', $modification_time); - # The token contains the old modification_time. We need a new one. - $cgi->param('token', issue_hash_token([$attachment->id, $modification_time])); - - $vars->{'attachment'} = $attachment; - - print $cgi->header(); - # Warn the user about the mid-air collision and ask them what to do. - $template->process("attachment/midair.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - exit; - } - } - } + my $user = Bugzilla->user; + my $dbh = Bugzilla->dbh; + + # Start a transaction in preparation for updating the attachment. + $dbh->bz_start_transaction(); + + # Retrieve and validate parameters + my $attachment = validateID(); + my $bug = $attachment->bug; + $attachment->_check_bug; + my $can_edit = $attachment->validate_can_edit; + + if ($can_edit) { + $attachment->set_description(scalar $cgi->param('description')); + $attachment->set_is_patch(scalar $cgi->param('ispatch')); + $attachment->set_content_type(scalar $cgi->param('contenttypeentry')); + $attachment->set_is_obsolete(scalar $cgi->param('isobsolete')); + $attachment->set_is_private(scalar $cgi->param('isprivate')); + $attachment->set_filename(scalar $cgi->param('filename')); + + # Now make sure the attachment has not been edited since we loaded the page. + my $delta_ts = $cgi->param('delta_ts'); + my $modification_time = $attachment->modification_time; + + if ($delta_ts && $delta_ts ne $modification_time) { + datetime_from($delta_ts) + or ThrowCodeError('invalid_timestamp', {timestamp => $delta_ts}); + ($vars->{'operations'}) = $bug->get_activity($attachment->id, $delta_ts); + + # If the modification date changed but there is no entry in + # the activity table, this means someone commented only. + # In this case, there is no reason to midair. + if (scalar(@{$vars->{'operations'}})) { + $cgi->param('delta_ts', $modification_time); + + # The token contains the old modification_time. We need a new one. + $cgi->param('token', issue_hash_token([$attachment->id, $modification_time])); + + $vars->{'attachment'} = $attachment; - # We couldn't do this check earlier as we first had to validate attachment ID - # and display the mid-air collision page if modification_time changed. - my $token = $cgi->param('token'); - check_hash_token($token, [$attachment->id, $attachment->modification_time]); - - # If the user submitted a comment while editing the attachment, - # add the comment to the bug. Do this after having validated isprivate! - my $comment = $cgi->param('comment'); - if (defined $comment && trim($comment) ne '') { - $bug->add_comment($comment, { isprivate => $attachment->isprivate, - type => CMT_ATTACHMENT_UPDATED, - extra_data => $attachment->id }); + print $cgi->header(); + + # Warn the user about the mid-air collision and ask them what to do. + $template->process("attachment/midair.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; + } } + } - $bug->add_cc($user) if $cgi->param('addselfcc'); + # We couldn't do this check earlier as we first had to validate attachment ID + # and display the mid-air collision page if modification_time changed. + my $token = $cgi->param('token'); + check_hash_token($token, [$attachment->id, $attachment->modification_time]); + + # If the user submitted a comment while editing the attachment, + # add the comment to the bug. Do this after having validated isprivate! + my $comment = $cgi->param('comment'); + if (defined $comment && trim($comment) ne '') { + $bug->add_comment( + $comment, + { + isprivate => $attachment->isprivate, + type => CMT_ATTACHMENT_UPDATED, + extra_data => $attachment->id + } + ); + } + + $bug->add_cc($user) if $cgi->param('addselfcc'); - my ($flags, $new_flags) = - Bugzilla::Flag->extract_flags_from_cgi($bug, $attachment, $vars); + my ($flags, $new_flags) + = Bugzilla::Flag->extract_flags_from_cgi($bug, $attachment, $vars); - if ($can_edit) { - $attachment->set_flags($flags, $new_flags); + if ($can_edit) { + $attachment->set_flags($flags, $new_flags); + } + + # Requestees can set flags targetted to them, even if they cannot + # edit the attachment. Flag setters can edit their own flags too. + elsif (scalar @$flags) { + my %flag_list = map { $_->{id} => $_ } @$flags; + my $flag_objs = Bugzilla::Flag->new_from_list([keys %flag_list]); + + my @editable_flags; + foreach my $flag_obj (@$flag_objs) { + if ($flag_obj->setter_id == $user->id + || ($flag_obj->requestee_id && $flag_obj->requestee_id == $user->id)) + { + push(@editable_flags, $flag_list{$flag_obj->id}); + } } - # Requestees can set flags targetted to them, even if they cannot - # edit the attachment. Flag setters can edit their own flags too. - elsif (scalar @$flags) { - my %flag_list = map { $_->{id} => $_ } @$flags; - my $flag_objs = Bugzilla::Flag->new_from_list([keys %flag_list]); - - my @editable_flags; - foreach my $flag_obj (@$flag_objs) { - if ($flag_obj->setter_id == $user->id - || ($flag_obj->requestee_id && $flag_obj->requestee_id == $user->id)) - { - push(@editable_flags, $flag_list{$flag_obj->id}); - } - } - if (scalar @editable_flags) { - $attachment->set_flags(\@editable_flags, []); - # Flag changes must be committed. - $can_edit = 1; - } + if (scalar @editable_flags) { + $attachment->set_flags(\@editable_flags, []); + + # Flag changes must be committed. + $can_edit = 1; } + } - # Figure out when the changes were made. - my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); + # Figure out when the changes were made. + my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); - # Commit the comment, if any. - # This has to happen before updating the attachment, to ensure new comments - # are available to $attachment->update. - $bug->update($timestamp); + # Commit the comment, if any. + # This has to happen before updating the attachment, to ensure new comments + # are available to $attachment->update. + $bug->update($timestamp); - if ($can_edit) { - my $changes = $attachment->update($timestamp); - # If there are changes, we updated delta_ts in the DB. We have to - # reflect this change in the bug object. - $bug->{delta_ts} = $timestamp if scalar(keys %$changes); - } + if ($can_edit) { + my $changes = $attachment->update($timestamp); - # Commit the transaction now that we are finished updating the database. - $dbh->bz_commit_transaction(); + # If there are changes, we updated delta_ts in the DB. We have to + # reflect this change in the bug object. + $bug->{delta_ts} = $timestamp if scalar(keys %$changes); + } - # Define the variables and functions that will be passed to the UI template. - $vars->{'attachment'} = $attachment; - $vars->{'bugs'} = [$bug]; - $vars->{'header_done'} = 1; - $vars->{'sent_bugmail'} = - Bugzilla::BugMail::Send($bug->id, { 'changer' => $user }); + # Commit the transaction now that we are finished updating the database. + $dbh->bz_commit_transaction(); - print $cgi->header(); + # Define the variables and functions that will be passed to the UI template. + $vars->{'attachment'} = $attachment; + $vars->{'bugs'} = [$bug]; + $vars->{'header_done'} = 1; + $vars->{'sent_bugmail'} + = Bugzilla::BugMail::Send($bug->id, {'changer' => $user}); - # Generate and return the UI (HTML page) from the appropriate template. - $template->process("attachment/updated.html.tmpl", $vars) - || ThrowTemplateError($template->error()); + print $cgi->header(); + + # Generate and return the UI (HTML page) from the appropriate template. + $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; + my $user = Bugzilla->login(LOGIN_REQUIRED); + my $dbh = Bugzilla->dbh; - print $cgi->header(); + print $cgi->header(); - $user->in_group('admin') - || ThrowUserError('auth_failure', {group => 'admin', - action => 'delete', - object => 'attachment'}); - - Bugzilla->params->{'allow_attachment_deletion'} - || ThrowUserError('attachment_deletion_disabled'); - - # Make sure the administrator is allowed to edit this attachment. - my $attachment = validateID(); - Bugzilla::Attachment->_check_bug($attachment->bug); - - $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 'delete_attachment' . $attachment->id)) - { - # The token is invalid. - ThrowUserError('token_does_not_exist'); - } + $user->in_group('admin') + || ThrowUserError('auth_failure', + {group => 'admin', action => 'delete', object => 'attachment'}); - my $bug = new Bugzilla::Bug($attachment->bug_id); + Bugzilla->params->{'allow_attachment_deletion'} + || ThrowUserError('attachment_deletion_disabled'); - # The token is valid. Delete the content of the attachment. - my $msg; - $vars->{'attachment'} = $attachment; - $vars->{'reason'} = clean_text($cgi->param('reason') || ''); + # Make sure the administrator is allowed to edit this attachment. + my $attachment = validateID(); + Bugzilla::Attachment->_check_bug($attachment->bug); - $template->process("attachment/delete_reason.txt.tmpl", $vars, \$msg) - || ThrowTemplateError($template->error()); + $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 'delete_attachment' . $attachment->id)) + { + # The token is invalid. + ThrowUserError('token_does_not_exist'); + } - # Paste the reason provided by the admin into a comment. - $bug->add_comment($msg); + my $bug = new Bugzilla::Bug($attachment->bug_id); - $attachment->remove_from_db(); + # The token is valid. Delete the content of the attachment. + my $msg; + $vars->{'attachment'} = $attachment; + $vars->{'reason'} = clean_text($cgi->param('reason') || ''); - # Now delete the token. - delete_token($token); + $template->process("attachment/delete_reason.txt.tmpl", $vars, \$msg) + || ThrowTemplateError($template->error()); - # Insert the comment. - $bug->update(); + # Paste the reason provided by the admin into a comment. + $bug->add_comment($msg); - # Required to display the bug the deleted attachment belongs to. - $vars->{'bugs'} = [$bug]; - $vars->{'header_done'} = 1; + $attachment->remove_from_db(); - $vars->{'sent_bugmail'} = - Bugzilla::BugMail::Send($bug->id, { 'changer' => $user }); + # Now delete the token. + delete_token($token); - $template->process("attachment/updated.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - } - else { - # Create a token. - $token = issue_session_token('delete_attachment' . $attachment->id); + # Insert the comment. + $bug->update(); - $vars->{'a'} = $attachment; - $vars->{'token'} = $token; + # Required to display the bug the deleted attachment belongs to. + $vars->{'bugs'} = [$bug]; + $vars->{'header_done'} = 1; - $template->process("attachment/confirm-delete.html.tmpl", $vars) - || ThrowTemplateError($template->error()); - } + $vars->{'sent_bugmail'} + = Bugzilla::BugMail::Send($bug->id, {'changer' => $user}); + + $template->process("attachment/updated.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + else { + # Create a token. + $token = issue_session_token('delete_attachment' . $attachment->id); + + $vars->{'a'} = $attachment; + $vars->{'token'} = $token; + + $template->process("attachment/confirm-delete.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } } |