]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 405444: FormatDouble and FormatTriple mangle multi-byte strings in email
authormkanat%bugzilla.org <>
Wed, 19 Dec 2007 03:22:43 +0000 (03:22 +0000)
committermkanat%bugzilla.org <>
Wed, 19 Dec 2007 03:22:43 +0000 (03:22 +0000)
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=himorin, a=mkanat

Bugzilla/BugMail.pm
Bugzilla/Util.pm

index 967ad8ca301ed27e29a30dbfb099a14562e731c7..b8680e10b0c7796da04a64d3af534bb98653387e 100644 (file)
@@ -46,6 +46,11 @@ use Bugzilla::Mailer;
 use Date::Parse;
 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_WATCHING  => 2;
 
@@ -60,25 +65,34 @@ use constant REL_NAMES => {
     REL_GLOBAL_WATCHER, "GlobalWatcher"
 };
 
-sub FormatTriple {
-    my ($a, $b, $c) = (@_);
-    $^A = "";
-    my $temp = formline << 'END', $a, $b, $c;
-^>>>>>>>>>>>>>>>>>>|^<<<<<<<<<<<<<<<<<<<<<<<<<<<|^<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
-END
-    ; # This semicolon appeases my emacs editor macros. :-)
-    return $^A;
+# We use this instead of format because format doesn't deal well with
+# multi-byte languages.
+sub multiline_sprintf {
+    my ($format, $args, $sizes) = @_;
+    my @parts;
+    my @my_sizes = @$sizes; # Copy this so we don't modify the input array.
+    while (my $string = shift @$args) {
+        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 {
-    my ($a, $b) = (@_);
-    $a .= ":";
-    $^A = "";
-    my $temp = formline << 'END', $a, $b;
-^>>>>>>>>>>>>>>>>>> ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
-END
-    ; # This semicolon appeases my emacs editor macros. :-)
-    return $^A;
+
+sub three_columns {
+    return multiline_sprintf(FORMAT_TRIPLE, \@_, FORMAT_3_SIZE);
 }
 
 # This is a bit of a hack, basically keeping the old system()
@@ -232,7 +246,7 @@ sub Send {
             $lastwho = $who;
             $fullwho = $whoname ? "$whoname <$who>" : $who;
             $diffheader = "\n$fullwho changed:\n\n";
-            $diffheader .= FormatTriple("What    ", "Removed", "Added");
+            $diffheader .= three_columns("What    ", "Removed", "Added");
             $diffheader .= ('-' x 76) . "\n";
         }
         $what =~ s/^(Attachment )?/Attachment #$attachid / if $attachid;
@@ -249,7 +263,7 @@ sub Send {
                 'SELECT isprivate FROM attachments WHERE attach_id = ?',
                 undef, ($attachid));
         }
-        $difftext = FormatTriple($what, $old, $new);
+        $difftext = three_columns($what, $old, $new);
         $diffpart->{'header'} = $diffheader;
         $diffpart->{'fieldname'} = $fieldname;
         $diffpart->{'text'} = $difftext;
@@ -303,11 +317,11 @@ sub Send {
                   "\nBug $id depends on bug $depbug, which changed state.\n\n" .
                   "Bug $depbug Summary: $summary\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";
                 $interestingchange = 0;
             }
-            $thisdiff .= FormatTriple($fielddescription{$what}, $old, $new);
+            $thisdiff .= three_columns($fielddescription{$what}, $old, $new);
             if ($what eq 'bug_status'
                 && is_open_state($old) ne is_open_state($new))
             {
@@ -546,7 +560,8 @@ sub sendMail {
              $user->groups->{Bugzilla->params->{'timetrackinggroup'}}) {
 
             my $desc = $fielddescription{$f};
-            $head .= FormatDouble($desc, $value);
+            $head .= multiline_sprintf(FORMAT_DOUBLE, ["$desc:", $value], 
+                                       FORMAT_2_SIZE);
         }
       }
     }
index aad2c5672843930833829a4d2d014b5bc48caac2..4d702f02e9b41414939892e6e1ec78921b32c1fc 100644 (file)
@@ -38,7 +38,7 @@ use base qw(Exporter);
                              i_am_cgi get_netaddr correct_urlbase
                              lsearch
                              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
                              validate_time
                              file_mod_time is_7bit_clean
@@ -339,6 +339,17 @@ sub find_wrap_point {
     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 {
     my ($date, $format) = @_;
 
@@ -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
 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)>
 
 Takes a bug comment, and wraps it to the appropriate length. The length is