Commit 59a35402 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 526158: Make email_in.pl use Bugzilla::Bug->create to create bugs instead of…

Bug 526158: Make email_in.pl use Bugzilla::Bug->create to create bugs instead of requiring post_bug.cgi Patch by Max Kanat-Alexander <mkanat@bugzilla.org> r=LpSolit, a=LpSolit
parent 56285b05
...@@ -236,6 +236,27 @@ use constant UPDATE_COMMENT_COLUMNS => qw( ...@@ -236,6 +236,27 @@ use constant UPDATE_COMMENT_COLUMNS => qw(
# activity table. # activity table.
use constant MAX_LINE_LENGTH => 254; use constant MAX_LINE_LENGTH => 254;
# This maps the names of internal Bugzilla bug fields to things that would
# make sense to somebody who's not intimately familiar with the inner workings
# of Bugzilla. (These are the field names that the WebService and email_in.pl
# use.)
use constant FIELD_MAP => {
creation_time => 'creation_ts',
description => 'comment',
id => 'bug_id',
last_change_time => 'delta_ts',
platform => 'rep_platform',
severity => 'bug_severity',
status => 'bug_status',
summary => 'short_desc',
url => 'bug_file_loc',
whiteboard => 'status_whiteboard',
# These are special values for the WebService Bug.search method.
limit => 'LIMIT',
offset => 'OFFSET',
};
##################################################################### #####################################################################
sub new { sub new {
...@@ -557,7 +578,6 @@ sub create { ...@@ -557,7 +578,6 @@ sub create {
return $bug; return $bug;
} }
sub run_create_validators { sub run_create_validators {
my $class = shift; my $class = shift;
my $params = $class->SUPER::run_create_validators(@_); my $params = $class->SUPER::run_create_validators(@_);
...@@ -1168,6 +1188,9 @@ sub _check_cc { ...@@ -1168,6 +1188,9 @@ sub _check_cc {
my ($invocant, $component, $ccs) = @_; my ($invocant, $component, $ccs) = @_;
return [map {$_->id} @{$component->initial_cc}] unless $ccs; return [map {$_->id} @{$component->initial_cc}] unless $ccs;
# Allow comma-separated input as well as arrayrefs.
$ccs = [split(/[\s,]+/, $ccs)] if !ref $ccs;
my %cc_ids; my %cc_ids;
foreach my $person (@$ccs) { foreach my $person (@$ccs) {
next unless $person; next unless $person;
...@@ -1732,6 +1755,17 @@ sub _check_freetext_field { ...@@ -1732,6 +1755,17 @@ sub _check_freetext_field {
sub _check_multi_select_field { sub _check_multi_select_field {
my ($invocant, $values, $field) = @_; my ($invocant, $values, $field) = @_;
# Allow users (mostly email_in.pl) to specify multi-selects as
# comma-separated values.
if (defined $values and !ref $values) {
# We don't split on spaces because multi-select values can and often
# do have spaces in them. (Theoretically they can have commas in them
# too, but that's much less common and people should be able to work
# around it pretty cleanly, if they want to use email_in.pl.)
$values = [split(',', $values)];
}
return [] if !$values; return [] if !$values;
my @checked_values; my @checked_values;
foreach my $value (@$values) { foreach my $value (@$values) {
...@@ -1865,6 +1899,7 @@ sub set_component { ...@@ -1865,6 +1899,7 @@ sub set_component {
} }
sub set_custom_field { sub set_custom_field {
my ($self, $field, $value) = @_; my ($self, $field, $value) = @_;
if (ref $value eq 'ARRAY' && $field->type != FIELD_TYPE_MULTI_SELECT) { if (ref $value eq 'ARRAY' && $field->type != FIELD_TYPE_MULTI_SELECT) {
$value = $value->[0]; $value = $value->[0];
} }
...@@ -3177,6 +3212,25 @@ sub LogActivityEntry { ...@@ -3177,6 +3212,25 @@ sub LogActivityEntry {
} }
} }
# Convert WebService API and email_in.pl field names to internal DB field
# names.
sub map_fields {
my ($params) = @_;
my %field_values;
foreach my $field (keys %$params) {
my $field_name = FIELD_MAP->{$field} || $field;
$field_values{$field_name} = $params->{$field};
}
# This protects the WebService Bug.search method.
unless (Bugzilla->user->is_timetracker) {
delete @field_values{qw(estimated_time remaining_time deadline)};
}
return \%field_values;
}
# CountOpenDependencies counts the number of open dependent bugs for a # CountOpenDependencies counts the number of open dependent bugs for a
# list of bugs and returns a list of bug_id's and their dependency count # list of bugs and returns a list of bug_id's and their dependency count
# It takes one parameter: # It takes one parameter:
......
...@@ -716,7 +716,14 @@ sub can_enter_product { ...@@ -716,7 +716,14 @@ sub can_enter_product {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$warn ||= 0; $warn ||= 0;
if (!defined $input) { $input = trim($input) if !ref $input;
if (!defined $input or $input eq '') {
return unless $warn == THROW_ERROR;
ThrowUserError('object_not_specified',
{ class => 'Bugzilla::Product' });
}
if (!scalar @{ $self->get_enterable_products }) {
return unless $warn == THROW_ERROR; return unless $warn == THROW_ERROR;
ThrowUserError('no_products'); ThrowUserError('no_products');
} }
......
...@@ -37,24 +37,6 @@ use Bugzilla::Util qw(trim); ...@@ -37,24 +37,6 @@ use Bugzilla::Util qw(trim);
# Constants # # Constants #
############# #############
# This maps the names of internal Bugzilla bug fields to things that would
# make sense to somebody who's not intimately familiar with the inner workings
# of Bugzilla. (These are the field names that the WebService uses.)
use constant FIELD_MAP => {
creation_time => 'creation_ts',
description => 'comment',
id => 'bug_id',
last_change_time => 'delta_ts',
platform => 'rep_platform',
severity => 'bug_severity',
status => 'bug_status',
summary => 'short_desc',
url => 'bug_file_loc',
whiteboard => 'status_whiteboard',
limit => 'LIMIT',
offset => 'OFFSET',
};
use constant PRODUCT_SPECIFIC_FIELDS => qw(version target_milestone component); use constant PRODUCT_SPECIFIC_FIELDS => qw(version target_milestone component);
use constant DATE_FIELDS => { use constant DATE_FIELDS => {
...@@ -251,7 +233,7 @@ sub search { ...@@ -251,7 +233,7 @@ sub search {
{ param => 'limit', function => 'Bug.search()' }); { param => 'limit', function => 'Bug.search()' });
} }
$params = _map_fields($params); $params = Bugzilla::Bug::map_fields($params);
delete $params->{WHERE}; delete $params->{WHERE};
# Do special search types for certain fields. # Do special search types for certain fields.
...@@ -286,7 +268,7 @@ sub search { ...@@ -286,7 +268,7 @@ sub search {
sub create { sub create {
my ($self, $params) = @_; my ($self, $params) = @_;
Bugzilla->login(LOGIN_REQUIRED); Bugzilla->login(LOGIN_REQUIRED);
$params = _map_fields($params); $params = Bugzilla::Bug::map_fields($params);
my $bug = Bugzilla::Bug->create($params); my $bug = Bugzilla::Bug->create($params);
Bugzilla::BugMail::Send($bug->bug_id, { changer => $bug->reporter->login }); Bugzilla::BugMail::Send($bug->bug_id, { changer => $bug->reporter->login });
return { id => $self->type('int', $bug->bug_id) }; return { id => $self->type('int', $bug->bug_id) };
...@@ -294,7 +276,8 @@ sub create { ...@@ -294,7 +276,8 @@ sub create {
sub legal_values { sub legal_values {
my ($self, $params) = @_; my ($self, $params) = @_;
my $field = FIELD_MAP->{$params->{field}} || $params->{field}; my $field = Bugzilla::Bug::FIELD_MAP->{$params->{field}}
|| $params->{field};
my @global_selects = Bugzilla->get_fields( my @global_selects = Bugzilla->get_fields(
{type => [FIELD_TYPE_SINGLE_SELECT, FIELD_TYPE_MULTI_SELECT]}); {type => [FIELD_TYPE_SINGLE_SELECT, FIELD_TYPE_MULTI_SELECT]});
...@@ -520,24 +503,6 @@ sub _attachment_to_hash { ...@@ -520,24 +503,6 @@ sub _attachment_to_hash {
}; };
} }
# Convert WebService API field names to internal DB field names.
# Used by create() and search().
sub _map_fields {
my ($params) = @_;
my %field_values;
foreach my $field (keys %$params) {
my $field_name = FIELD_MAP->{$field} || $field;
$field_values{$field_name} = $params->{$field};
}
unless (Bugzilla->user->is_timetracker) {
delete @field_values{qw(estimated_time remaining_time deadline)};
}
return \%field_values;
}
1; 1;
__END__ __END__
......
...@@ -45,12 +45,13 @@ use Encode; ...@@ -45,12 +45,13 @@ use Encode;
use Bugzilla; use Bugzilla;
use Bugzilla::Bug; use Bugzilla::Bug;
use Bugzilla::BugMail;
use Bugzilla::Constants qw(USAGE_MODE_EMAIL); use Bugzilla::Constants qw(USAGE_MODE_EMAIL);
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Mailer; use Bugzilla::Mailer;
use Bugzilla::Token;
use Bugzilla::User; use Bugzilla::User;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Token;
############# #############
# Constants # # Constants #
...@@ -74,9 +75,6 @@ sub parse_mail { ...@@ -74,9 +75,6 @@ sub parse_mail {
my %fields; my %fields;
# Email::Address->parse returns an array
my ($reporter) = Email::Address->parse($input_email->header('From'));
$fields{'reporter'} = $reporter->address;
my $summary = $input_email->header('Subject'); my $summary = $input_email->header('Subject');
if ($summary =~ /\[\S+ (\d+)\](.*)/i) { if ($summary =~ /\[\S+ (\d+)\](.*)/i) {
$fields{'bug_id'} = $1; $fields{'bug_id'} = $1;
...@@ -107,19 +105,8 @@ sub parse_mail { ...@@ -107,19 +105,8 @@ sub parse_mail {
# Otherwise, we stop parsing fields on the first blank line. # Otherwise, we stop parsing fields on the first blank line.
$line = trim($line); $line = trim($line);
last if !$line; last if !$line;
if ($line =~ /^\@(\S+)\s*(?:=|\s)\s*(.*)\s*/) {
if ($line =~ /^@(\S+)\s*=\s*(.*)\s*/) {
$current_field = lc($1); $current_field = lc($1);
# It's illegal to pass the reporter field as you could
# override the "From:" field of the message and bypass
# authentication checks, such as PGP.
if ($current_field eq 'reporter') {
# We reset the $current_field variable to something
# post_bug and process_bug will ignore, in case the
# attacker splits the reporter field on several lines.
$current_field = 'illegal_field';
next;
}
$fields{$current_field} = $2; $fields{$current_field} = $2;
} }
else { else {
...@@ -128,6 +115,10 @@ sub parse_mail { ...@@ -128,6 +115,10 @@ sub parse_mail {
} }
} }
%fields = %{ Bugzilla::Bug::map_fields(\%fields) };
my ($reporter) = Email::Address->parse($input_email->header('From'));
$fields{'reporter'} = $reporter->address;
# The summary line only affects us if we're doing a post_bug. # The summary line only affects us if we're doing a post_bug.
# We have to check it down here because there might have been # We have to check it down here because there might have been
...@@ -150,24 +141,24 @@ sub parse_mail { ...@@ -150,24 +141,24 @@ sub parse_mail {
} }
sub post_bug { sub post_bug {
my ($fields_in) = @_; my ($fields) = @_;
my %fields = %$fields_in;
debug_print('Posting a new bug...'); debug_print('Posting a new bug...');
my $cgi = Bugzilla->cgi; # Bugzilla::Bug->create throws a confusing CodeError if
foreach my $field (keys %fields) { # the REQUIRED_CREATE_FIELDS are missing, but much more
$cgi->param(-name => $field, -value => $fields{$field}); # sensible errors if the fields exist but are just undef.
foreach my $field (Bugzilla::Bug::REQUIRED_CREATE_FIELDS) {
$fields->{$field} = undef if !exists $fields->{$field};
} }
$cgi->param(-name => 'inbound_email', -value => 1); my $bug = Bugzilla::Bug->create($fields);
debug_print("Created bug " . $bug->id);
require 'post_bug.cgi'; Bugzilla::BugMail::Send($bug->id, { changer => $bug->reporter->login });
debug_print("Sent bugmail");
} }
sub process_bug { sub process_bug {
my ($fields_in) = @_; my ($fields_in) = @_;
my %fields = %$fields_in; my %fields = %$fields_in;
my $bug_id = $fields{'bug_id'}; my $bug_id = $fields{'bug_id'};
...@@ -340,7 +331,6 @@ pod2usage({-verbose => 0, -exitval => 1}) if $switch{'help'}; ...@@ -340,7 +331,6 @@ pod2usage({-verbose => 0, -exitval => 1}) if $switch{'help'};
Bugzilla->usage_mode(USAGE_MODE_EMAIL); Bugzilla->usage_mode(USAGE_MODE_EMAIL);
my @mail_lines = <STDIN>; my @mail_lines = <STDIN>;
my $mail_text = join("", @mail_lines); my $mail_text = join("", @mail_lines);
my $mail_fields = parse_mail($mail_text); my $mail_fields = parse_mail($mail_text);
...@@ -351,9 +341,7 @@ if (my $suffix = Bugzilla->params->{'emailsuffix'}) { ...@@ -351,9 +341,7 @@ if (my $suffix = Bugzilla->params->{'emailsuffix'}) {
$username =~ s/\Q$suffix\E$//i; $username =~ s/\Q$suffix\E$//i;
} }
my $user = Bugzilla::User->new({ name => $username }) my $user = Bugzilla::User->check($username);
|| ThrowUserError('invalid_username', { name => $username });
Bugzilla->set_user($user); Bugzilla->set_user($user);
if ($mail_fields->{'bug_id'}) { if ($mail_fields->{'bug_id'}) {
...@@ -391,9 +379,9 @@ The script expects to read an email with the following format: ...@@ -391,9 +379,9 @@ The script expects to read an email with the following format:
From: account@domain.com From: account@domain.com
Subject: Bug Summary Subject: Bug Summary
@product = ProductName @product ProductName
@component = ComponentName @component ComponentName
@version = 1.0 @version 1.0
This is a bug description. It will be entered into the bug exactly as This is a bug description. It will be entered into the bug exactly as
written here. written here.
...@@ -404,39 +392,25 @@ The script expects to read an email with the following format: ...@@ -404,39 +392,25 @@ The script expects to read an email with the following format:
This is a signature line, and will be removed automatically, It will not This is a signature line, and will be removed automatically, It will not
be included in the bug description. be included in the bug description.
The C<@> labels can be any valid field name in Bugzilla that can be For the list of valid field names for the C<@> fields, including
set on C<enter_bug.cgi>. For the list of required field names, see a list of which ones are required, see L<Bugzilla::WebService::Bug/create>.
L<Bugzilla::WebService::Bug/Create>. Note, that there is some difference (Note, however, that you cannot specify C<@description> as a field--
in the names of the required input fields between web and email interfaces, you just add a comment by adding text after the C<@> fields.)
as listed below:
=over
=item *
C<platform> in web is C<@rep_platform> in email
=item *
C<severity> in web is C<@bug_severity> in email
=back
For the list of all field names, see the C<fielddefs> table in the database.
The values for the fields can be split across multiple lines, but The values for the fields can be split across multiple lines, but
note that a newline will be parsed as a single space, for the value. note that a newline will be parsed as a single space, for the value.
So, for example: So, for example:
@short_desc = This is a very long @summary This is a very long
description description
Will be parsed as "This is a very long description". Will be parsed as "This is a very long description".
If you specify C<@short_desc>, it will override the summary you specify If you specify C<@summary>, it will override the summary you specify
in the Subject header. in the Subject header.
C<account@domain.com> must be a valid Bugzilla account. C<account@domain.com> (the value of the C<From> header) must be a valid
Bugzilla account.
Note that signatures must start with '-- ', the standard signature Note that signatures must start with '-- ', the standard signature
border. border.
...@@ -453,11 +427,11 @@ Your subject starts with [Bug 123456] -- then it modifies bug 123456. ...@@ -453,11 +427,11 @@ Your subject starts with [Bug 123456] -- then it modifies bug 123456.
=item * =item *
You include C<@bug_id = 123456> in the first lines of the email. You include C<@id 123456> in the first lines of the email.
=back =back
If you do both, C<@bug_id> takes precedence. If you do both, C<@id> takes precedence.
You send your email in the same format as for creating a bug, except You send your email in the same format as for creating a bug, except
that you only specify the fields you want to change. If the very that you only specify the fields you want to change. If the very
...@@ -466,7 +440,7 @@ will be assumed that you are only adding a comment to the bug. ...@@ -466,7 +440,7 @@ will be assumed that you are only adding a comment to the bug.
Note that when updating a bug, the C<Subject> header is ignored, Note that when updating a bug, the C<Subject> header is ignored,
except for getting the bug ID. If you want to change the bug's summary, except for getting the bug ID. If you want to change the bug's summary,
you have to specify C<@short_desc> as one of the fields to change. you have to specify C<@summary> as one of the fields to change.
Please remember not to include any extra text in your emails, as that Please remember not to include any extra text in your emails, as that
text will also be added as a comment. This includes any text that your text will also be added as a comment. This includes any text that your
...@@ -476,8 +450,6 @@ another email. ...@@ -476,8 +450,6 @@ another email.
=head3 Adding/Removing CCs =head3 Adding/Removing CCs
To add CCs, you can specify them in a comma-separated list in C<@cc>. To add CCs, you can specify them in a comma-separated list in C<@cc>.
For backward compatibility, C<@newcc> can also be used. If both are
present, C<@cc> takes precedence.
To remove CCs, specify them as a comma-separated list in C<@removecc>. To remove CCs, specify them as a comma-separated list in C<@removecc>.
......
...@@ -264,13 +264,8 @@ if ($token) { ...@@ -264,13 +264,8 @@ if ($token) {
("createbug:$id", $token)); ("createbug:$id", $token));
} }
if (Bugzilla->usage_mode == USAGE_MODE_EMAIL) { print $cgi->header();
Bugzilla::BugMail::Send($id, $vars->{'mailrecipients'}); $template->process("bug/create/created.html.tmpl", $vars)
}
else {
print $cgi->header();
$template->process("bug/create/created.html.tmpl", $vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
}
1; 1;
...@@ -1741,6 +1741,8 @@ ...@@ -1741,6 +1741,8 @@
flagtype flagtype
[% ELSIF class == "Bugzilla::Field" %] [% ELSIF class == "Bugzilla::Field" %]
field field
[% ELSIF class == "Bugzilla::Product" %]
product
[% ELSIF class == "Bugzilla::Search::Saved" %] [% ELSIF class == "Bugzilla::Search::Saved" %]
saved search saved search
[% ELSIF ( matches = class.match('^Bugzilla::Field::Choice::(.+)') ) %] [% ELSIF ( matches = class.match('^Bugzilla::Field::Choice::(.+)') ) %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment