X-Git-Url: https://git.donarmstrong.com/?p=debbugs.git;a=blobdiff_plain;f=Debbugs%2FControl.pm;h=416abc150104741bff85625bb03353ab0930a3dd;hp=4d44237aa8a0b93e0b8acc9eab7be892239ac700;hb=4118f90e5b711616eeff28aef36615b0a892b57f;hpb=c72ff2efb30890213e4b6097bea521a96a72b665 diff --git a/Debbugs/Control.pm b/Debbugs/Control.pm index 4d44237..416abc1 100644 --- a/Debbugs/Control.pm +++ b/Debbugs/Control.pm @@ -75,7 +75,7 @@ is true, the above options must be present, and their values are used. use warnings; use strict; use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT); -use base qw(Exporter); +use Exporter qw(import); BEGIN{ $VERSION = 1.00; @@ -87,6 +87,7 @@ BEGIN{ severity => [qw(set_severity)], affects => [qw(affects)], summary => [qw(summary)], + outlook => [qw(outlook)], owner => [qw(owner)], title => [qw(set_title)], forward => [qw(set_forwarded)], @@ -99,6 +100,7 @@ BEGIN{ clone => [qw(clone_bug)], archive => [qw(bug_archive bug_unarchive), ], + limit => [qw(check_limit)], log => [qw(append_action_to_log), ], ); @@ -109,6 +111,7 @@ BEGIN{ use Debbugs::Config qw(:config); use Debbugs::Common qw(:lock buglog :misc get_hashname sort_versions); +use Debbugs::UTF8; use Debbugs::Status qw(bug_archiveable :read :hook writebug new_bug splitpackages split_status_fields get_bug_status); use Debbugs::CGI qw(html_escape); use Debbugs::Log qw(:misc :write); @@ -123,7 +126,7 @@ use IO::File; use Debbugs::Text qw(:templates); -use Debbugs::Mail qw(rfc822_date send_mail_message default_headers); +use Debbugs::Mail qw(rfc822_date send_mail_message default_headers encode_headers); use Debbugs::MIME qw(create_mime_message); use Mail::RFC822::Address qw(); @@ -132,6 +135,7 @@ use POSIX qw(strftime); use Storable qw(dclone nfreeze); use List::Util qw(first max); +use Encode qw(encode_utf8); use Carp; @@ -459,8 +463,7 @@ sub set_blocks { push @changed, 'removed blocking bug(s) of '.$data->{bug_num}.': '.english_join([keys %removed_blockers]) if keys %removed_blockers; $action = ucfirst(join ('; ',@changed)) if @changed; if (not @changed) { - print {$transcript} "Ignoring request to alter blocking bugs of bug #$data->{bug_num} to the same blocks previously set\n" - unless __internal_request(); + print {$transcript} "Ignoring request to alter blocking bugs of bug #$data->{bug_num} to the same blocks previously set\n"; next; } $data->{blockedby} = join(' ',keys %blockers); @@ -696,8 +699,7 @@ sub set_tag { push @changed, 'removed tag(s) '.english_join([keys %tag_removed]) if keys %tag_removed; $action = ucfirst(join ('; ',@changed)) if @changed; if (not @changed) { - print {$transcript} "Ignoring request to alter tags of bug #$data->{bug_num} to the same tags previously set\n" - unless __internal_request(); + print {$transcript} "Ignoring request to alter tags of bug #$data->{bug_num} to the same tags previously set\n"; next; } $action .= '.'; @@ -953,8 +955,8 @@ sub set_done { for my $data (@data) { my $old_data = dclone($data); my $hash = get_hashname($data->{bug_num}); - my $report_fh = IO::File->new("db-h/$hash/$data->{bug_num}.report",'r') or - die "Unable to open original report db-h/$hash/$data->{bug_num}.report for reading: $!"; + my $report_fh = IO::File->new("$config{spool_dir}/db-h/$hash/$data->{bug_num}.report",'r') or + die "Unable to open original report $config{spool_dir}/db-h/$hash/$data->{bug_num}.report for reading: $!"; my $orig_report; { local $/; @@ -998,7 +1000,7 @@ sub set_done { headers => [To => $data->{submitter}, Subject => "$config{ubug}#$data->{bug_num} ". - "closed by $param{requester} ($param{request_subject})", + "closed by $param{requester} ".(defined $param{request_subject}?"($param{request_subject})":""), ], ) ], @@ -1109,13 +1111,12 @@ sub set_submitter { (not defined $data->{originator} or not length $data->{originator})) or (defined $param{submitter} and defined $data->{originator} and $param{submitter} eq $data->{originator})) { - print {$transcript} "Ignoring request to change the submitter of bug#$data->{bug_num} to the same value\n" - unless __internal_request(); + print {$transcript} "Ignoring request to change the submitter of bug#$data->{bug_num} to the same value\n"; next; } else { if (defined $data->{originator} and length($data->{originator})) { - $action= "Changed $config{bug} submitter to '$param{submitter}' from '$data->{originator}'"; + $action= "Changed $config{bug} submitter to '$param{submitter}' from '$data->{originator}'."; $notify_old_submitter = 1; } else { @@ -1222,8 +1223,7 @@ sub set_forwarded { if (__all_undef_or_equal($param{forwarded},$data->{forwarded}) or (not defined $param{forwarded} and defined $data->{forwarded} and not length $data->{forwarded})) { - print {$transcript} "Ignoring request to change the forwarded-to-address of bug#$data->{bug_num} to the same value\n" - unless __internal_request(); + print {$transcript} "Ignoring request to change the forwarded-to-address of bug#$data->{bug_num} to the same value\n"; next; } else { @@ -1231,7 +1231,7 @@ sub set_forwarded { $action= "Unset $config{bug} forwarded-to-address"; } elsif (defined $data->{forwarded} and length($data->{forwarded})) { - $action= "Changed $config{bug} forwarded-to-address to '$param{forwarded}' from '$data->{forwarded}'"; + $action= "Changed $config{bug} forwarded-to-address to '$param{forwarded}' from '$data->{forwarded}'."; } else { $action= "Set $config{bug} forwarded-to-address to '$param{forwarded}'."; @@ -1311,13 +1311,12 @@ sub set_title { print {$debug} "Going to change bug title\n"; if (defined $data->{subject} and length($data->{subject}) and $data->{subject} eq $param{title}) { - print {$transcript} "Ignoring request to change the title of bug#$data->{bug_num} to the same title\n" - unless __internal_request(); + print {$transcript} "Ignoring request to change the title of bug#$data->{bug_num} to the same title\n"; next; } else { if (defined $data->{subject} and length($data->{subject})) { - $action= "Changed $config{bug} title to '$param{title}' from '$data->{subject}'"; + $action= "Changed $config{bug} title to '$param{title}' from '$data->{subject}'."; } else { $action= "Set $config{bug} title to '$param{title}'."; } @@ -1416,8 +1415,7 @@ sub set_package { print {$debug} "Going to change assigned package\n"; if (defined $data->{package} and length($data->{package}) and $data->{package} eq $new_package) { - print {$transcript} "Ignoring request to reassign bug #$data->{bug_num} to the same package\n" - unless __internal_request(); + print {$transcript} "Ignoring request to reassign bug #$data->{bug_num} to the same package\n"; next; } else { @@ -1581,6 +1579,16 @@ sub set_found { if (not @svers) { @svers = $version; } + elsif (not grep {$version eq $_} @svers) { + # The $version was not equal to one of the source + # versions, so it's probably unqualified (or just + # wrong). Delete it, and use the source versions + # instead. + if (exists $found_versions{$version}) { + delete $found_versions{$version}; + $found_removed{$version} = 1; + } + } for my $sver (@svers) { if (not exists $found_versions{$sver}) { $found_versions{$sver} = 1; @@ -1588,7 +1596,7 @@ sub set_found { } # if the found we are adding matches any fixed # versions, remove them - my @temp = grep m{(^|/)\Q$sver\E}, keys %fixed_versions; + my @temp = grep m{(^|/)\Q$sver\E$}, keys %fixed_versions; delete $fixed_versions{$_} for @temp; $fixed_removed{$_} = 1 for @temp; } @@ -1612,7 +1620,7 @@ sub set_found { # in the case of removal, we only concern ourself with # the version passed, not the source version it maps # to - my @temp = grep m{(^|/)\Q$version\E}, keys %found_versions; + my @temp = grep m{(?:^|/)\Q$version\E$}, keys %found_versions; delete $found_versions{$_} for @temp; $found_removed{$_} = 1 for @temp; } @@ -1649,8 +1657,7 @@ sub set_found { $action .= " and reopened" } if (not $reopened and not @changed) { - print {$transcript} "Ignoring request to alter found versions of bug #$data->{bug_num} to the same values previously set\n" - unless __internal_request(); + print {$transcript} "Ignoring request to alter found versions of bug #$data->{bug_num} to the same values previously set\n"; next; } $action .= '.'; @@ -1793,6 +1800,12 @@ sub set_fixed { if (not @svers) { @svers = $version; } + else { + if (exists $fixed_versions{$version}) { + $fixed_removed{$version} = 1; + delete $fixed_versions{$version}; + } + } for my $sver (@svers) { if (not exists $fixed_versions{$sver}) { $fixed_versions{$sver} = 1; @@ -1858,8 +1871,7 @@ sub set_fixed { $action .= " and reopened" } if (not $reopened and not @changed) { - print {$transcript} "Ignoring request to alter fixed versions of bug #$data->{bug_num} to the same values previously set\n" - unless __internal_request(); + print {$transcript} "Ignoring request to alter fixed versions of bug #$data->{bug_num} to the same values previously set\n"; next; } $action .= '.'; @@ -2015,7 +2027,7 @@ sub set_merged { $new_locks += $n_locks; %data = %{$data}; @data = values %data; - if (not __check_limit(data => [@data], + if (not check_limit(data => [@data], exists $param{limit}?(limit => $param{limit}):(), transcript => $transcript, )) { @@ -2075,7 +2087,7 @@ sub set_merged { # figure out the problems print {$transcript} "Unable to merge bugs because:\n"; for my $change (@{$disallowed_changes}) { - print {$transcript} "$change->{field} of #$change->{bug} is '$change->{orig_value}' not '$change->{value}'\n"; + print {$transcript} "$change->{field} of #$change->{bug} is '$change->{text_orig_value}' not '$change->{text_value}'\n"; } if ($attempts > 0) { croak "Some bugs were altered while attempting to merge"; @@ -2084,66 +2096,69 @@ sub set_merged { croak "Did not alter merged bugs"; } } - my ($change_bug) = keys %{$changes}; - $bug_changed{$change_bug}++; - print {$transcript} __bug_info($data{$change_bug}) if - $param{show_bug_info} and not __internal_request(1); - $bug_info_shown{$change_bug} = 1; - __allow_relocking($param{locks},[keys %data]); - for my $change (@{$changes->{$change_bug}}) { - if ($change->{field} eq 'blockedby' or $change->{field} eq 'blocks') { - my %target_blockedby; - @target_blockedby{@{$change->{func_value}}} = (1) x @{$change->{func_value}}; - my %unhandled_targets = %target_blockedby; - my @blocks_to_remove; - for my $key (split / /,$change->{orig_value}) { - delete $unhandled_targets{$key}; - next if exists $target_blockedby{$key}; - set_blocks(bug => $change->{field} eq 'blocks' ? $key : $change->{bug}, - block => $change->{field} eq 'blocks' ? $change->{bug} : $key, - remove => 1, - hash_slice(%param, - keys %common_options, - keys %append_action_options), - ); + my @bugs_to_change = keys %{$changes}; + for my $change_bug (@bugs_to_change) { + next unless exists $changes->{$change_bug}; + $bug_changed{$change_bug}++; + print {$transcript} __bug_info($data{$change_bug}) if + $param{show_bug_info} and not __internal_request(1); + $bug_info_shown{$change_bug} = 1; + __allow_relocking($param{locks},[keys %data]); + for my $change (@{$changes->{$change_bug}}) { + if ($change->{field} eq 'blockedby' or $change->{field} eq 'blocks') { + my %target_blockedby; + @target_blockedby{@{$change->{func_value}}} = (1) x @{$change->{func_value}}; + my %unhandled_targets = %target_blockedby; + my @blocks_to_remove; + for my $key (split / /,$change->{orig_value}) { + delete $unhandled_targets{$key}; + next if exists $target_blockedby{$key}; + set_blocks(bug => $change->{field} eq 'blocks' ? $key : $change->{bug}, + block => $change->{field} eq 'blocks' ? $change->{bug} : $key, + remove => 1, + hash_slice(%param, + keys %common_options, + keys %append_action_options), + ); + } + for my $key (keys %unhandled_targets) { + set_blocks(bug => $change->{field} eq 'blocks' ? $key : $change->{bug}, + block => $change->{field} eq 'blocks' ? $change->{bug} : $key, + add => 1, + hash_slice(%param, + keys %common_options, + keys %append_action_options), + ); + } } - for my $key (keys %unhandled_targets) { - set_blocks(bug => $change->{field} eq 'blocks' ? $key : $change->{bug}, - block => $change->{field} eq 'blocks' ? $change->{bug} : $key, - add => 1, - hash_slice(%param, - keys %common_options, - keys %append_action_options), - ); + else { + $change->{function}->(bug => $change->{bug}, + $change->{key}, $change->{func_value}, + exists $change->{options}?@{$change->{options}}:(), + hash_slice(%param, + keys %common_options, + keys %append_action_options), + ); } } - else { - $change->{function}->(bug => $change->{bug}, - $change->{key}, $change->{func_value}, - exists $change->{options}?@{$change->{options}}:(), - hash_slice(%param, - keys %common_options, - keys %append_action_options), - ); - } + __disallow_relocking($param{locks}); + my ($data,$n_locks) = + __lock_and_load_merged_bugs(bugs_to_load => [keys %merging], + data => \@data, + locks => $param{locks}, + debug => $debug, + reload_all => 1, + ); + $new_locks += $n_locks; + $locks += $n_locks; + %data = %{$data}; + @data = values %data; + ($merge_status,$bugs_to_merge) = + __calculate_merge_status(\@data,\%data,$param{bug},$merge_status); + ($disallowed_changes,$changes) = + __calculate_merge_changes(\@data,$merge_status,\%param); + $attempts = max(values %bug_changed); } - __disallow_relocking($param{locks}); - my ($data,$n_locks) = - __lock_and_load_merged_bugs(bugs_to_load => [keys %merging], - data => \@data, - locks => $param{locks}, - debug => $debug, - reload_all => 1, - ); - $new_locks += $n_locks; - $locks += $n_locks; - %data = %{$data}; - @data = values %data; - ($merge_status,$bugs_to_merge) = - __calculate_merge_status(\@data,\%data,$param{bug}); - ($disallowed_changes,$changes) = - __calculate_merge_changes(\@data,$merge_status,\%param); - $attempts = max(values %bug_changed); } if ($param{show_bug_info} and not __internal_request(1)) { for my $data (sort {$a->{bug_num} <=> $b->{bug_num}} @data) { @@ -2152,12 +2167,16 @@ sub set_merged { } } if (keys %{$changes} or @{$disallowed_changes}) { - print {$transcript} "Unable to modify bugs so that they could be merged\n"; + print {$transcript} "After four attempts, the following changes were unable to be made:\n"; for (1..$new_locks) { unfilelock($param{locks}); $locks--; } __end_control(%info); + for my $change ((map {@{$_}} values %{$changes}), @{$disallowed_changes}) { + print {$transcript} "$change->{field} of #$change->{bug} is '$change->{text_orig_value}' not '$change->{text_value}'\n"; + } + die "Unable to modify bugs so they could be merged"; return; } @@ -2191,8 +2210,9 @@ sub set_merged { sub __allow_relocking{ my ($locks,$bugs) = @_; - for my $bug (@{$bugs}) { - my @lockfiles = grep {m{/\Q$bug\E$}} keys %{$locks->{locks}}; + my @locks = (@{$bugs},'merge'); + for my $lock (@locks) { + my @lockfiles = grep {m{/\Q$lock\E$}} keys %{$locks->{locks}}; next unless @lockfiles; $locks->{relockable}{$lockfiles[0]} = 0; } @@ -2273,8 +2293,8 @@ sub __lock_and_load_merged_bugs{ sub __calculate_merge_status{ - my ($data_a,$data_h,$master_bug,$merge) = @_; - my %merge_status; + my ($data_a,$data_h,$master_bug,$merge_status) = @_; + my %merge_status = %{$merge_status // {}}; my %merged_bugs; my $bugs_to_merge = 0; for my $data (@{$data_a}) { @@ -2288,13 +2308,13 @@ sub __calculate_merge_status{ # look like. However, if merge is set, tags, fixed and found # are merged. if ($data->{bug_num} == $master_bug) { - for (qw(package forwarded severity blocks blockedby done owner summary affects)) { + for (qw(package forwarded severity blocks blockedby done owner summary outlook affects)) { $merge_status{$_} = $data->{$_} } } - if (not $merge) { - next unless $data->{bug_num} == $master_bug; - } + if (defined $merge_status) { + next unless $data->{bug_num} == $master_bug; + } $merge_status{tag} = {} if not exists $merge_status{tag}; for my $tag (split /\s+/, $data->{keywords}) { $merge_status{tag}{$tag} = 1; @@ -2304,6 +2324,17 @@ sub __calculate_merge_status{ @{$merge_status{"${_}_versions"}}{@{$data->{"${_}_versions"}}} = (1) x @{$data->{"${_}_versions"}}; } } + # if there is a non-source qualified version with a corresponding + # source qualified version, we only want to merge the source + # qualified version(s) + for (qw(fixed found)) { + my @unqualified_versions = grep {m{/}?0:1} keys %{$merge_status{"${_}_versions"}}; + for my $unqualified_version (@unqualified_versions) { + if (grep {m{/\Q$unqualified_version\E}} keys %{$merge_status{"${_}_versions"}}) { + delete $merge_status{"${_}_versions"}{$unqualified_version}; + } + } + } return (\%merge_status,$bugs_to_merge); } @@ -2358,6 +2389,10 @@ sub __calculate_merge_changes{ key => 'summary', options => [], }, + outlook => {func => \&outlook, + key => 'outlook', + options => [], + }, affects => {func => \&affects, key => 'package', options => [], @@ -2382,7 +2417,7 @@ sub __calculate_merge_changes{ allowed => 1, }, ); - for my $field (qw(forwarded severity blocks blockedby done owner summary affects package fixed_versions found_versions keywords)) { + for my $field (qw(forwarded severity blocks blockedby done owner summary outlook affects package fixed_versions found_versions keywords)) { # if the ideal bug already has the field set properly, we # continue on. if ($field eq 'keywords'){ @@ -2393,6 +2428,19 @@ sub __calculate_merge_changes{ next if join(' ', sort @{$data->{$field}}) eq join(' ',sort keys %{$merge_status->{$field}}); } + elsif ($field eq 'done') { + # for done, we only care if the bug is done or not + # done, not the value it's set to. + if (defined $merge_status->{$field} and length $merge_status->{$field} and + defined $data->{$field} and length $data->{$field}) { + next; + } + elsif ((not defined $merge_status->{$field} or not length $merge_status->{$field}) and + (not defined $data->{$field} or not length $data->{$field}) + ) { + next; + } + } elsif ($merge_status->{$field} eq $data->{$field}) { next; } @@ -2408,10 +2456,12 @@ sub __calculate_merge_changes{ function => $force_functions{$field}{func}, key => $force_functions{$field}{key}, options => $force_functions{$field}{options}, - allowed => exists $force_functions{$field}{allowed} ? 0 : $force_functions{$field}{allowed}, + allowed => exists $force_functions{$field}{allowed} ? $force_functions{$field}{allowed} : 0, }; - if ($param->{force}) { - if ($field ne 'package') { + $change->{text_value} = ref($change->{func_value}) eq 'ARRAY'?join(' ',@{$change->{func_value}}):$change->{func_value}; + $change->{text_orig_value} = ref($change->{orig_value}) eq 'ARRAY'?join(' ',@{$change->{orig_value}}):$change->{orig_value}; + if ($param->{force} or $change->{allowed}) { + if ($field ne 'package' or $change->{allowed}) { push @{$changes{$data->{bug_num}}},$change; next; } @@ -2565,8 +2615,7 @@ sub affects { } } if (not length $action) { - print {$transcript} "Ignoring request to set affects of bug $data->{bug_num} to the same value previously set\n" - unless __internal_request(); + print {$transcript} "Ignoring request to set affects of bug $data->{bug_num} to the same value previously set\n"; next; } my $old_data = dclone($data); @@ -2614,7 +2663,7 @@ Handles all setting of summary fields If summary is undef, unsets the summary -If summary is 0, sets the summary to the first paragraph contained in +If summary is 0 or -1, sets the summary to the first paragraph contained in the message passed. If summary is a positive integer, sets the summary to the message specified. @@ -2625,23 +2674,66 @@ Otherwise, sets summary to the value passed. sub summary { - my %param = validate_with(params => \@_, + # outlook and summary are exactly the same, basically + return _summary('summary',@_); +} + +=head1 OUTLOOK FUNCTIONS + +=head2 outlook + + eval { + outlook(bug => $ref, + transcript => $transcript, + ($dl > 0 ? (debug => $transcript):()), + requester => $header{from}, + request_addr => $controlrequestaddr, + message => \@log, + affected_packages => \%affected_packages, + recipients => \%recipients, + outlook => undef, + ); + }; + if ($@) { + $errors++; + print {$transcript} "Failed to mark $ref with outlook foo: $@"; + } + +Handles all setting of outlook fields + +If outlook is undef, unsets the outlook + +If outlook is 0, sets the outlook to the first paragraph contained in +the message passed. + +If outlook is a positive integer, sets the outlook to the message specified. + +Otherwise, sets outlook to the value passed. + +=cut + + +sub outlook { + return _summary('outlook',@_); +} + +sub _summary { + my ($cmd,@params) = @_; + my %param = validate_with(params => \@params, spec => {bug => {type => SCALAR, regex => qr/^\d+$/, }, # specific options here - summary => {type => SCALAR|UNDEF, - default => 0, - }, + $cmd , {type => SCALAR|UNDEF, + default => 0, + }, %common_options, %append_action_options, }, ); -# croak "summary must be numeric or undef" if -# defined $param{summary} and not $param{summary} =~ /^\d+/; my %info = __begin_control(%param, - command => 'summary' + command => $cmd, ); my ($debug,$transcript) = @info{qw(debug transcript)}; @@ -2651,27 +2743,27 @@ sub summary { my $summary = ''; my $summary_msg = ''; my $action = ''; - if (not defined $param{summary}) { + if (not defined $param{$cmd}) { # do nothing - print {$debug} "Removing summary fields\n"; - $action = 'Removed summary'; + print {$debug} "Removing $cmd fields\n"; + $action = "Removed $cmd"; } - elsif ($param{summary} =~ /^\d+$/) { + elsif ($param{$cmd} =~ /^-?\d+$/) { my $log = []; my @records = Debbugs::Log::read_log_records(bug_num => $param{bug}); - if ($param{summary} == 0) { + if ($param{$cmd} == 0 or $param{$cmd} == -1) { $log = $param{message}; $summary_msg = @records + 1; } else { - if (($param{summary} - 1 ) > $#records) { - die "Message number '$param{summary}' exceeds the maximum message '$#records'"; + if (($param{$cmd} - 1 ) > $#records) { + die "Message number '$param{$cmd}' exceeds the maximum message '$#records'"; } - my $record = $records[($param{summary} - 1 )]; + my $record = $records[($param{$cmd} - 1 )]; if ($record->{type} !~ /incoming-recv|recips/) { - die "Message number '$param{summary}' is a invalid message type '$record->{type}'"; + die "Message number '$param{$cmd}' is a invalid message type '$record->{type}'"; } - $summary_msg = $param{summary}; + $summary_msg = $param{$cmd}; $log = [$record->{text}]; } my $p_o = Debbugs::MIME::parse(join('',@{$log})); @@ -2693,7 +2785,7 @@ sub summary { } # skip a paragraph if it looks like it's control or # pseudo-headers - if ($line =~ m{^\s*(?:Package|Source|Version|User|Tag|Severity)\:\s+\S}xi or #pseudo headers + if ($line =~ m{^\s*(?:Package|Source|Version|User|Tag|Severity|Control)\:\s+\S}xi or #pseudo headers $line =~ m{^(?:package:?|(?:no|)owner|severity|tags?|summary| #control \#|reopen|close|(?:not|)(?:fixed|found)|clone| debug|(?:not|)forwarded|priority| @@ -2710,39 +2802,38 @@ sub summary { next if $in_pseudoheaders; $paragraph .= $line ." \n"; } - print {$debug} "Summary is going to be '$paragraph'\n"; + print {$debug} ucfirst($cmd)." is going to be '$paragraph'\n"; $summary = $paragraph; $summary =~ s/[\n\r]/ /g; if (not length $summary) { - die "Unable to find summary message to use"; + die "Unable to find $cmd message to use"; } # trim off a trailing spaces $summary =~ s/\ *$//; } else { - $summary = $param{summary}; + $summary = $param{$cmd}; } for my $data (@data) { - print {$debug} "Going to change summary\n"; + print {$debug} "Going to change $cmd\n"; if (((not defined $summary or not length $summary) and - (not defined $data->{summary} or not length $data->{summary})) or - $summary eq $data->{summary}) { - print {$transcript} "Ignoring request to change the summary of bug $param{bug} to the same value\n" - unless __internal_request(); + (not defined $data->{$cmd} or not length $data->{$cmd})) or + $summary eq $data->{$cmd}) { + print {$transcript} "Ignoring request to change the $cmd of bug $param{bug} to the same value\n"; next; } if (length $summary) { - if (length $data->{summary}) { - $action = "Summary replaced with message bug $param{bug} message $summary_msg"; + if (length $data->{$cmd}) { + $action = ucfirst($cmd)." replaced with message bug $param{bug} message $summary_msg"; } else { - $action = "Summary recorded from message bug $param{bug} message $summary_msg"; + $action = ucfirst($cmd)." recorded from message bug $param{bug} message $summary_msg"; } } my $old_data = dclone($data); - $data->{summary} = $summary; + $data->{$cmd} = $summary; append_action_to_log(bug => $data->{bug_num}, - command => 'summary', + command => $cmd, old_data => $old_data, new_data => $data, get_lock => 0, @@ -2872,19 +2963,19 @@ sub clone_bug { # bugs that this bug is blocking are also blocked by the new clone(s) for my $bug (split ' ', $data->{blocks}) { for my $new_bug (@new_bugs) { - set_blocks(bug => $new_bug, - blocks => $bug, + set_blocks(bug => $bug, + block => $new_bug, hash_slice(%param, keys %common_options, keys %append_action_options), ); } } - # bugs that this bug is blocked by are also blocking the new clone(s) + # bugs that are blocking this bug are also blocking the new clone(s) for my $bug (split ' ', $data->{blockedby}) { for my $new_bug (@new_bugs) { - set_blocks(bug => $bug, - blocks => $new_bug, + set_blocks(bug => $new_bug, + block => $bug, hash_slice(%param, keys %common_options, keys %append_action_options), @@ -2945,8 +3036,7 @@ sub owner { print {$debug} "Owner is currently '$data->{owner}' for bug $data->{bug_num}\n"; if (not defined $param{owner} or not length $param{owner}) { if (not defined $data->{owner} or not length $data->{owner}) { - print {$transcript} "Ignoring request to unset the owner of bug #$data->{bug_num} which was not set\n" - unless __internal_request(); + print {$transcript} "Ignoring request to unset the owner of bug #$data->{bug_num} which was not set\n"; next; } $param{owner} = ''; @@ -3057,7 +3147,6 @@ sub bug_archive { print {$transcript} "Bug $param{bug} cannot be archived\n"; die "Bug $param{bug} cannot be archived"; } - print {$debug} "$param{bug} considering\n"; if (not $param{archive_unarchived} and not exists $data[0]{unarchived} ) { @@ -3332,7 +3421,7 @@ sub append_action_to_log{ $nd{$key} = $new_data->{$key}; # $data_diff .= html_escape("$Debbugs::Status::fields{$key}: $new_data->{$key}")."\n"; } - $data_diff .= html_escape(Data::Dumper->Dump([\%nd],[qw(new_data)])); + $data_diff .= html_escape(Data::Dumper->Dump([encode_utf8_structure(\%nd)],[qw(new_data)])); $data_diff .= "-->\n"; $data_diff .= "\n"; } my $msg = join('', (exists $param{command} ? - "\n":"" + "\n":"" ), (length $param{requester} ? - "\n":"" + "\n":"" ), (length $param{request_addr} ? - "\n":"" + "\n":"" ), "\n", $data_diff, - "".html_escape($param{action})."\n"); + "".html_escape(encode_utf8_safely($param{action}))."\n"); if (length $param{requester}) { - $msg .= "Request was from ".html_escape($param{requester})."\n"; + $msg .= "Request was from ".html_escape(encode_utf8_safely($param{requester}))."\n"; } if (length $param{request_addr}) { - $msg .= "to ".html_escape($param{request_addr}).""; + $msg .= "to ".html_escape(encode_utf8_safely($param{request_addr})).""; } if (length $param{desc}) { - $msg .= ":
\n$param{desc}\n"; + $msg .= ":
\n".encode_utf8_safely($param{desc})."\n"; } else { $msg .= ".\n"; @@ -3378,7 +3467,7 @@ sub append_action_to_log{ $msg = ''; if ((ref($param{message}) and @{$param{message}}) or length($param{message})) { push @records, {type => exists $param{recips}?'recips':'incoming-recv', - exists $param{recips}?(recips => [make_list($param{recips})]):(), + exists $param{recips}?(recips => [map {encode_utf8_safely($_)} make_list($param{recips})]):(), text => join('',make_list($param{message})), }; } @@ -3502,13 +3591,14 @@ sub __return_append_to_log_options{ } if (not exists $param{message}) { my $date = rfc822_date(); - $param{message} = fill_in_template(template => 'mail/fake_control_message', - variables => {request_addr => $param{request_addr}, - requester => $param{requester}, - date => $date, - action => $action - }, - ); + $param{message} = + encode_headers(fill_in_template(template => 'mail/fake_control_message', + variables => {request_addr => $param{request_addr}, + requester => $param{requester}, + date => $date, + action => $action + }, + )); } if (not defined $action) { carp "Undefined action!"; @@ -3571,7 +3661,8 @@ sub __begin_control { ); my $new_locks; my ($debug,$transcript) = __handle_debug_transcript(@_); - print {$debug} "$param{bug} considering\n"; + print {$debug} "considering bug $param{bug} for ".(exists $param{command}?$param{command}:scalar caller())."\n"; +# print {$debug} Data::Dumper->Dump([[caller(1)],\%param],[qw(caller param)])."\n"; $lockhash = $param{locks} if exists $param{locks}; my @data = (); my $old_die = $SIG{__DIE__}; @@ -3593,7 +3684,7 @@ sub __begin_control { } } } - if (not __check_limit(data => \@data, + if (not check_limit(data => \@data, exists $param{limit}?(limit => $param{limit}):(), transcript => $transcript, )) { @@ -3660,9 +3751,9 @@ sub __end_control { } -=head2 __check_limit +=head2 check_limit - __check_limit(data => \@data, limit => $param{limit}); + check_limit(data => \@data, limit => $param{limit}); Checks to make sure that bugs match any limits; each entry of @data @@ -3679,9 +3770,9 @@ limit to succeed. =cut -sub __check_limit{ +sub check_limit{ my %param = validate_with(params => \@_, - spec => {data => {type => ARRAYREF|SCALAR, + spec => {data => {type => ARRAYREF|HASHREF, }, limit => {type => HASHREF|UNDEF, }, @@ -3773,7 +3864,9 @@ sub __message_body_template{ my $hole_var = {'&bugurl' => sub{"$_[0]: ". 'http://'.$config{cgi_domain}.'/'. - Debbugs::CGI::bug_url($_[0]); + Debbugs::CGI::bug_links(bug => $_[0], + links_only => 1, + ); } };