Commit d3108642 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 405444: FormatDouble and FormatTriple mangle multi-byte strings in email

Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=himorin, a=mkanat
parent 2fe815e9
...@@ -46,6 +46,11 @@ use Bugzilla::Mailer; ...@@ -46,6 +46,11 @@ use Bugzilla::Mailer;
use Date::Parse; use Date::Parse;
use Date::Format; use Date::Format;
use constant FORMAT_TRIPLE => "%19s|%-28s|%-28s";
use constant FORMAT_3_SIZE => [19,28,28];
use constant FORMAT_DOUBLE => "%19s %-55s";
use constant FORMAT_2_SIZE => [19,55];
use constant BIT_DIRECT => 1; use constant BIT_DIRECT => 1;
use constant BIT_WATCHING => 2; use constant BIT_WATCHING => 2;
...@@ -60,25 +65,34 @@ use constant REL_NAMES => { ...@@ -60,25 +65,34 @@ use constant REL_NAMES => {
REL_GLOBAL_WATCHER, "GlobalWatcher" REL_GLOBAL_WATCHER, "GlobalWatcher"
}; };
sub FormatTriple { # We use this instead of format because format doesn't deal well with
my ($a, $b, $c) = (@_); # multi-byte languages.
$^A = ""; sub multiline_sprintf {
my $temp = formline << 'END', $a, $b, $c; my ($format, $args, $sizes) = @_;
^>>>>>>>>>>>>>>>>>>|^<<<<<<<<<<<<<<<<<<<<<<<<<<<|^<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ my @parts;
END my @my_sizes = @$sizes; # Copy this so we don't modify the input array.
; # This semicolon appeases my emacs editor macros. :-) while (my $string = shift @$args) {
return $^A; my $size = shift @my_sizes;
my @pieces = split("\n", wrap_hard($string, $size));
push(@parts, \@pieces);
}
my $formatted;
while (1) {
# Get the first item of each part.
my @line = map { shift @$_ } @parts;
# If they're all undef, we're done.
last if !grep { defined $_ } @line;
# Make any single undef item into ''
@line = map { defined $_ ? $_ : '' } @line;
# And append a formatted line
$formatted .= sprintf("$format\n", @line);
}
return $formatted;
} }
sub FormatDouble { sub three_columns {
my ($a, $b) = (@_); return multiline_sprintf(FORMAT_TRIPLE, \@_, FORMAT_3_SIZE);
$a .= ":";
$^A = "";
my $temp = formline << 'END', $a, $b;
^>>>>>>>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
END
; # This semicolon appeases my emacs editor macros. :-)
return $^A;
} }
# This is a bit of a hack, basically keeping the old system() # This is a bit of a hack, basically keeping the old system()
...@@ -232,7 +246,7 @@ sub Send { ...@@ -232,7 +246,7 @@ sub Send {
$lastwho = $who; $lastwho = $who;
$fullwho = $whoname ? "$whoname <$who>" : $who; $fullwho = $whoname ? "$whoname <$who>" : $who;
$diffheader = "\n$fullwho changed:\n\n"; $diffheader = "\n$fullwho changed:\n\n";
$diffheader .= FormatTriple("What ", "Removed", "Added"); $diffheader .= three_columns("What ", "Removed", "Added");
$diffheader .= ('-' x 76) . "\n"; $diffheader .= ('-' x 76) . "\n";
} }
$what =~ s/^(Attachment )?/Attachment #$attachid / if $attachid; $what =~ s/^(Attachment )?/Attachment #$attachid / if $attachid;
...@@ -249,7 +263,7 @@ sub Send { ...@@ -249,7 +263,7 @@ sub Send {
'SELECT isprivate FROM attachments WHERE attach_id = ?', 'SELECT isprivate FROM attachments WHERE attach_id = ?',
undef, ($attachid)); undef, ($attachid));
} }
$difftext = FormatTriple($what, $old, $new); $difftext = three_columns($what, $old, $new);
$diffpart->{'header'} = $diffheader; $diffpart->{'header'} = $diffheader;
$diffpart->{'fieldname'} = $fieldname; $diffpart->{'fieldname'} = $fieldname;
$diffpart->{'text'} = $difftext; $diffpart->{'text'} = $difftext;
...@@ -303,11 +317,11 @@ sub Send { ...@@ -303,11 +317,11 @@ sub Send {
"\nBug $id depends on bug $depbug, which changed state.\n\n" . "\nBug $id depends on bug $depbug, which changed state.\n\n" .
"Bug $depbug Summary: $summary\n" . "Bug $depbug Summary: $summary\n" .
"${urlbase}show_bug.cgi?id=$depbug\n\n"; "${urlbase}show_bug.cgi?id=$depbug\n\n";
$thisdiff .= FormatTriple("What ", "Old Value", "New Value"); $thisdiff .= three_columns("What ", "Old Value", "New Value");
$thisdiff .= ('-' x 76) . "\n"; $thisdiff .= ('-' x 76) . "\n";
$interestingchange = 0; $interestingchange = 0;
} }
$thisdiff .= FormatTriple($fielddescription{$what}, $old, $new); $thisdiff .= three_columns($fielddescription{$what}, $old, $new);
if ($what eq 'bug_status' if ($what eq 'bug_status'
&& is_open_state($old) ne is_open_state($new)) && is_open_state($old) ne is_open_state($new))
{ {
...@@ -546,7 +560,8 @@ sub sendMail { ...@@ -546,7 +560,8 @@ sub sendMail {
$user->groups->{Bugzilla->params->{'timetrackinggroup'}}) { $user->groups->{Bugzilla->params->{'timetrackinggroup'}}) {
my $desc = $fielddescription{$f}; my $desc = $fielddescription{$f};
$head .= FormatDouble($desc, $value); $head .= multiline_sprintf(FORMAT_DOUBLE, ["$desc:", $value],
FORMAT_2_SIZE);
} }
} }
} }
......
...@@ -38,7 +38,7 @@ use base qw(Exporter); ...@@ -38,7 +38,7 @@ use base qw(Exporter);
i_am_cgi get_netaddr correct_urlbase i_am_cgi get_netaddr correct_urlbase
lsearch lsearch
diff_arrays diff_strings diff_arrays diff_strings
trim wrap_comment find_wrap_point trim wrap_hard wrap_comment find_wrap_point
format_time format_time_decimal validate_date format_time format_time_decimal validate_date
validate_time validate_time
file_mod_time is_7bit_clean file_mod_time is_7bit_clean
...@@ -339,6 +339,17 @@ sub find_wrap_point { ...@@ -339,6 +339,17 @@ sub find_wrap_point {
return $wrappoint; return $wrappoint;
} }
sub wrap_hard {
my ($string, $columns) = @_;
local $Text::Wrap::columns = $columns;
local $Text::Wrap::unexpand = 0;
local $Text::Wrap::huge = 'wrap';
my $wrapped = wrap('', '', $string);
chomp($wrapped);
return $wrapped;
}
sub format_time { sub format_time {
my ($date, $format) = @_; my ($date, $format) = @_;
...@@ -739,6 +750,11 @@ compared to the old one. Returns a list, where the first entry is a scalar ...@@ -739,6 +750,11 @@ compared to the old one. Returns a list, where the first entry is a scalar
containing removed items, and the second entry is a scalar containing added containing removed items, and the second entry is a scalar containing added
items. items.
=item C<wrap_hard($string, $size)>
Wraps a string, so that a line is I<never> longer than C<$size>.
Returns the string, wrapped.
=item C<wrap_comment($comment)> =item C<wrap_comment($comment)>
Takes a bug comment, and wraps it to the appropriate length. The length is Takes a bug comment, and wraps it to the appropriate length. The length is
......
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