]> git.donarmstrong.com Git - debbugs.git/commitdiff
Merge branch 'master' into don/processcleanup
authorDon Armstrong <don@donarmstrong.com>
Sun, 8 Jul 2012 18:23:18 +0000 (11:23 -0700)
committerDon Armstrong <don@donarmstrong.com>
Sun, 8 Jul 2012 18:23:18 +0000 (11:23 -0700)
18 files changed:
Debbugs/CGI/Bugreport.pm
Debbugs/Common.pm
Debbugs/Control.pm
Debbugs/Log.pm
Debbugs/MIME.pm
Debbugs/Mail.pm
Debbugs/SOAP.pm
Debbugs/Status.pm
Debbugs/Text.pm
cgi/bugreport.cgi
cgi/pkgreport.cgi
debian/changelog
scripts/process
t/01_mime.t
t/02_common.t [new file with mode: 0644]
t/05_mail.t
t/13_utf8_mail.t [new file with mode: 0644]
t/lib/DebbugsTest.pm

index 02e6fba3a84431f773ab6077349bd549f64d29cc..d8bdb15952af71d0d921873e2435f40a10f3c017 100644 (file)
@@ -37,6 +37,7 @@ use Debbugs::CGI qw(:url :html :util);
 use Debbugs::Common qw(globify_scalar english_join);
 use Debbugs::Config qw(:config);
 use POSIX qw(strftime);
+use Encode qw(decode_utf8);
 
 BEGIN{
      ($VERSION) = q$Revision: 494 $ =~ /^Revision:\s+([^\s+])/;
@@ -156,10 +157,14 @@ sub display_entity {
            my $head = $entity->head;
            chomp(my $type = $entity->effective_type);
            my $body = $entity->stringify_body;
+           # this attachment has its own content type, so we must not
+           # try to convert it to UTF-8 or do anything funky.
+           my @layers = PerlIO::get_layers($param{output});
+           binmode($param{output},':raw');
            print {$param{output}} "Content-Type: $type";
            my ($charset) = $head->get('Content-Type:') =~ m/charset\s*=\s*\"?([\w-]+)\"?/i;
            print {$param{output}} qq(; charset="$charset") if defined $charset;
-           print {$param{output}}"\n";
+           print {$param{output}} "\n";
            if ($filename ne '') {
                my $qf = $filename;
                $qf =~ s/"/\\"/g;
@@ -169,6 +174,9 @@ sub display_entity {
            print {$param{output}} "\n";
            my $decoder = MIME::Decoder->new($head->mime_encoding);
            $decoder->decode(IO::Scalar->new(\$body), $param{output});
+           if (grep {/utf8/} @layers) {
+               binmode($param{output},':utf8');
+           }
            return;
        }
        elsif (not exists $param{att}) {
@@ -232,7 +240,7 @@ sub display_entity {
         my $content_type = $entity->head->get('Content-Type:') || "text/html";
         my ($charset) = $content_type =~ m/charset\s*=\s*\"?([\w-]+)\"?/i;
         my $body = $entity->bodyhandle->as_string;
-        $body = convert_to_utf8($body,$charset) if defined $charset;
+        $body = convert_to_utf8($body,$charset//'utf8');
         $body = html_escape($body);
         # Attempt to deal with format=flowed
         if ($content_type =~ m/format\s*=\s*\"?flowed\"?/i) {
@@ -282,7 +290,9 @@ appropriate.
 sub handle_email_message{
      my ($email,%param) = @_;
 
-     my $output = '';
+     # output needs to have the is_utf8 flag on to avoid double
+     # encoding
+     my $output = decode_utf8('');
      my $parser = MIME::Parser->new();
      # Because we are using memory, not tempfiles, there's no need to
      # clean up here like in Debbugs::MIME
@@ -318,12 +328,16 @@ should be output to the browser.
 sub handle_record{
      my ($record,$bug_number,$msg_number,$seen_msg_ids) = @_;
 
-     my $output = '';
+     # output needs to have the is_utf8 flag on to avoid double
+     # encoding
+     my $output = decode_utf8('');
      local $_ = $record->{type};
      if (/html/) {
-         my ($time) = $record->{text} =~ /<!--\s+time:(\d+)\s+-->/;
-         my $class = $record->{text} =~ /^<strong>(?:Acknowledgement|Reply|Information|Report|Notification)/m ? 'infmessage':'msgreceived';
-         $output .= decode_rfc1522($record->{text});
+        # $record->{text} is not in perl's internal encoding; convert it
+        my $text = decode_utf8($record->{text});
+         my ($time) = $text =~ /<!--\s+time:(\d+)\s+-->/;
+         my $class = $text =~ /^<strong>(?:Acknowledgement|Reply|Information|Report|Notification)/m ? 'infmessage':'msgreceived';
+         $output .= decode_rfc1522($text);
          # Link to forwarded http:// urls in the midst of the report
          # (even though these links already exist at the top)
          $output =~ s,((?:ftp|http|https)://[\S~-]+?/?)((?:[\)\'\:\.\,]|\&\#39;)?(?:\s|\.<|$)),<a href=\"$1\">$1</a>$2,go;
index 880989843d1cf32f7e52cd1bae317cada4cee633..f3c8218ea66cf1cc8e63e6ac77e22fd31d5a2f47 100644 (file)
@@ -50,12 +50,13 @@ BEGIN{
                                qw(cleanup_eval_fail),
                                qw(hash_slice),
                               ],
+                    utf8   => [qw(encode_utf8_structure)],
                     date   => [qw(secs_to_english)],
                     quit   => [qw(quit)],
                     lock   => [qw(filelock unfilelock lockpid)],
                    );
      @EXPORT_OK = ();
-     Exporter::export_ok_tags(qw(lock quit date util misc));
+     Exporter::export_ok_tags(keys %EXPORT_TAGS);
      $EXPORT_TAGS{all} = [@EXPORT_OK];
 }
 
@@ -70,6 +71,8 @@ use IO::Scalar;
 use Debbugs::MIME qw(decode_rfc1522);
 use Mail::Address;
 use Cwd qw(cwd);
+use Encode qw(encode_utf8 is_utf8);
+use Storable qw(dclone);
 
 use Params::Validate qw(validate_with :types);
 
@@ -429,6 +432,7 @@ sub __add_to_hash {
     $type //= 'address';
     my $fh = IO::File->new($fn,'r') or
        die "Unable to open $fn for reading: $!";
+    binmode($fh,':encoding(UTF-8)');
     while (<$fh>) {
        chomp;
        next unless m/^(\S+)\s+(\S.*\S)\s*$/;
@@ -823,7 +827,8 @@ sub globify_scalar {
          if (defined ref($scalar)) {
               if (ref($scalar) eq 'SCALAR' and
                   not UNIVERSAL::isa($scalar,'GLOB')) {
-                   return IO::Scalar->new($scalar);
+                   open $handle, '>:scalar:utf8', $scalar;
+                   return $handle;
               }
               else {
                    return $scalar;
@@ -836,7 +841,7 @@ sub globify_scalar {
               carp "Given a non-scalar reference, non-glob to globify_scalar; returning /dev/null handle";
          }
      }
-     return IO::File->new('/dev/null','w');
+     return IO::File->new('/dev/null','>:utf8');
 }
 
 =head2 cleanup_eval_fail()
@@ -894,6 +899,56 @@ sub hash_slice(\%@) {
     return map {exists $hashref->{$_}?($_,$hashref->{$_}):()} @keys;
 }
 
+
+=head1 UTF-8
+
+These functions are exported with the :utf8 tag
+
+=head2 encode_utf8_structure
+
+     %newdata = encode_utf8_structure(%newdata);
+
+Takes a complex data structure and encodes any strings with is_utf8
+set into their constituent octets.
+
+=cut
+
+our $depth = 0;
+sub encode_utf8_structure {
+    ++$depth;
+    my @ret;
+    for my $_ (@_) {
+       if (ref($_) eq 'HASH') {
+           push @ret, {encode_utf8_structure(%{$depth == 1 ? dclone($_):$_})};
+       }
+       elsif (ref($_) eq 'ARRAY') {
+           push @ret, [encode_utf8_structure(@{$depth == 1 ? dclone($_):$_})];
+       }
+       elsif (ref($_)) {
+           # we don't know how to handle non hash or non arrays
+           push @ret,$_;
+       }
+       else {
+           push @ret,__encode_utf8($_);
+       }
+    }
+    --$depth;
+    return @ret;
+}
+
+sub __encode_utf8 {
+    my @ret;
+    for my $r (@_) {
+       if (not ref($r) and is_utf8($r)) {
+           $r = encode_utf8($r);
+       }
+       push @ret,$r;
+    }
+    return @ret;
+}
+
+
+
 1;
 
 __END__
index ed69c84bd4147a3824a4709713937d315ccf8a1c..6851656f49c6b4d4bde5c4c857cbb8584a2cde0a 100644 (file)
@@ -108,7 +108,7 @@ BEGIN{
 }
 
 use Debbugs::Config qw(:config);
-use Debbugs::Common qw(:lock buglog :misc get_hashname sort_versions);
+use Debbugs::Common qw(:lock buglog :misc get_hashname sort_versions :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);
@@ -132,6 +132,7 @@ use POSIX qw(strftime);
 
 use Storable qw(dclone nfreeze);
 use List::Util qw(first max);
+use Encode qw(encode_utf8);
 
 use Carp;
 
@@ -996,7 +997,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})":""),
                                                             ],
                                                            )
                                            ],
@@ -3355,7 +3356,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 .= "<!-- old_data:\n";
         my %od;
@@ -3367,30 +3368,30 @@ sub append_action_to_log{
             $od{$key} = $old_data->{$key};
             # $data_diff .= html_escape("$Debbugs::Status::fields{$key}: $old_data->{$key}")."\n";
         }
-        $data_diff .= html_escape(Data::Dumper->Dump([\%od],[qw(old_data)]));
+        $data_diff .= html_escape(Data::Dumper->Dump([encode_utf8_structure(\%od)],[qw(old_data)]));
         $data_diff .= "-->\n";
      }
      my $msg = join('',
                    (exists $param{command} ?
-                    "<!-- command:".html_escape($param{command})." -->\n":""
+                    "<!-- command:".html_escape(encode_utf8($param{command}))." -->\n":""
                    ),
                    (length $param{requester} ?
-                    "<!-- requester: ".html_escape($param{requester})." -->\n":""
+                    "<!-- requester: ".html_escape(encode_utf8($param{requester}))." -->\n":""
                    ),
                    (length $param{request_addr} ?
-                    "<!-- request_addr: ".html_escape($param{request_addr})." -->\n":""
+                    "<!-- request_addr: ".html_escape(encode_utf8($param{request_addr}))." -->\n":""
                    ),
                    "<!-- time:".time()." -->\n",
                    $data_diff,
-                   "<strong>".html_escape($param{action})."</strong>\n");
+                   "<strong>".html_escape(encode_utf8($param{action}))."</strong>\n");
      if (length $param{requester}) {
-          $msg .= "Request was from <code>".html_escape($param{requester})."</code>\n";
+          $msg .= "Request was from <code>".html_escape(encode_utf8($param{requester}))."</code>\n";
      }
      if (length $param{request_addr}) {
-          $msg .= "to <code>".html_escape($param{request_addr})."</code>";
+          $msg .= "to <code>".html_escape(encode_utf8($param{request_addr}))."</code>";
      }
      if (length $param{desc}) {
-         $msg .= ":<br>\n$param{desc}\n";
+         $msg .= ":<br>\n".encode_utf8($param{desc})."\n";
      }
      else {
          $msg .= ".\n";
index a28384de59bd5ddaf9ce41d8b32f7b8dbd504779..2ae7af7b919705b1e859f55cfc686a0741ab980c 100644 (file)
@@ -39,7 +39,7 @@ use Carp;
 
 use Debbugs::Common qw(getbuglocation getbugcomponent make_list);
 use Params::Validate qw(:types validate_with);
-use Encode qw(encode);
+use Encode qw(encode is_utf8);
 
 =head1 NAME
 
@@ -51,6 +51,11 @@ The Debbugs::Log module provides a convenient way for scripts to read and
 write the .log files used by debbugs to store the complete textual records
 of all bug transactions.
 
+Debbugs::Log does not decode utf8 into perl's internal encoding or
+encode into utf8 from perl's internal encoding. For html records and
+all recips, this should probably be done. For other records, this should
+not be needed.
+
 =head2 The .log File Format
 
 .log files consist of a sequence of records, of one of the following four
@@ -383,6 +388,9 @@ sub write_log_records
     for my $record (@records) {
        my $type = $record->{type};
        croak "record type '$type' with no text field" unless defined $record->{text};
+       # I am not sure if we really want to croak here; but this is
+       # almost certainly a bug if is_utf8 is on.
+       # croak "probably wrong encoding" if is_utf8($record->{text});
        my ($text) = escape_log($record->{text});
        if ($type eq 'autocheck') {
            print {$logfh} "\01\n$text\03\n" or
@@ -427,7 +435,7 @@ Applies the log escape regex to the passed logfile.
 
 sub escape_log {
        my @log = @_;
-       return map { eval {$_ = encode("utf8",$_,Encode::FB_CROAK)}; s/^([\01-\07\030])/\030$1/gm; $_ } @log;
+       return map {s/^([\01-\07\030])/\030$1/gm; $_ } @log;
 }
 
 
index c2faeaa8621b3f5c580beb5c450425c1d39f9b6c..05534e3e054c47d2cb281f683aaacf1a0ac7a52e 100644 (file)
@@ -229,9 +229,7 @@ sub convert_to_utf8 {
      return $data if $charset eq 'raw' or is_utf8($data,1);
      my $result;
      eval {
-         # this encode/decode madness is to make sure that the data
-         # really is valid utf8 and that the is_utf8 flag is off.
-         $result = encode("utf8",decode($charset,$data))
+        $result = decode($charset,$data);
      };
      if ($@) {
          warn "Unable to decode charset; '$charset' and '$data': $@";
@@ -286,6 +284,9 @@ sub encode_rfc1522 {
 
      # handle being passed undef properly
      return undef if not defined $rawstr;
+     if (is_utf8($rawstr)) {
+        $rawstr= encode_utf8($rawstr);
+     }
      # We process words in reverse so we can preserve spacing between
      # encoded words. This regex splits on word|nonword boundaries and
      # nonword|nonword boundaries. We also consider parenthesis and "
@@ -313,7 +314,7 @@ sub encode_rfc1522 {
               if (length $encoded > 75) {
                    # Turn utf8 into the internal perl representation
                    # so . is a character, not a byte.
-                   my $tempstr = decode_utf8($word,Encode::FB_DEFAULT);
+                   my $tempstr = is_utf8($word)?$word:decode_utf8($word,Encode::FB_DEFAULT);
                    my @encoded;
                    # Strip it into 10 character long segments, and encode
                    # the segments
index 1366be3a33c09905272ebdd831a4396262563814..d26c860a612f5866576e56944e5c2c529e7eda0c 100644 (file)
@@ -357,11 +357,6 @@ sub send_mail_message{
      if ($param{encode_headers}) {
          $param{message} = encode_headers($param{message});
      }
-     eval {
-        if (is_utf8($param{message})) {
-            $param{message} = encode('utf8',$param{message});
-        }
-     };
 
      # First, try to send the message as is.
      eval {
index db066bfd870d4c67902b3f51ad5a2b349556b072..5cb08bb0f11eff7de6ab49dea880731a13dcb8b3 100644 (file)
@@ -24,6 +24,7 @@ None known.
 use warnings;
 use strict;
 use vars qw($DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
+use Debbugs::SOAP::Server;
 use base qw(Exporter SOAP::Server::Parameters);
 
 BEGIN{
@@ -38,12 +39,12 @@ BEGIN{
 
 }
 
-
 use IO::File;
 use Debbugs::Status qw(get_bug_status);
-use Debbugs::Common qw(make_list getbuglocation getbugcomponent);
+use Debbugs::Common qw(make_list getbuglocation getbugcomponent :utf8);
 use Debbugs::Packages;
-use Storable qw(nstore retrieve);
+
+use Storable qw(nstore retrieve dclone);
 use Scalar::Util qw(looks_like_number);
 
 
@@ -75,7 +76,7 @@ sub get_usertag {
               delete $ut{$tag} unless exists $tags{$tag};
          }
      }
-     return \%ut;
+     return encode_utf8_structure(\%ut);
 }
 
 
@@ -142,7 +143,7 @@ sub get_status {
          }
      }
 #     __prepare_response($self);
-     return \%status;
+     return encode_utf8_structure(\%status);
 }
 
 =head2 get_bugs
@@ -172,7 +173,7 @@ sub get_bugs{
      my %params = __collapse_params(@params);
      my @bugs;
      @bugs = Debbugs::Bugs::get_bugs(%params);
-     return \@bugs;
+     return encode_utf8_structure(\@bugs);
 }
 
 =head2 newest_bugs
@@ -188,7 +189,7 @@ sub newest_bugs{
      my $VERSION = __populate_version(pop);
      my ($self,$num) = @_;
      my $newest_bug = Debbugs::Bugs::newest_bug();
-     return [($newest_bug - $num + 1) .. $newest_bug];
+     return encode_utf8_structure([($newest_bug - $num + 1) .. $newest_bug]);
 
 }
 
@@ -249,7 +250,7 @@ sub get_bug_log{
                          msg_num => $current_msg,
                         };
      }
-     return \@messages;
+     return encode_utf8_structure(\@messages);
 }
 
 =head2 binary_to_source
@@ -276,13 +277,13 @@ sub binary_to_source{
      my ($self,@params) = @_;
 
      if ($VERSION <= 1) {
-        return [Debbugs::Packages::binary_to_source(binary => $params[0],
+        return encode_utf8_structure([Debbugs::Packages::binary_to_source(binary => $params[0],
                                                     (@params > 1)?(version => $params[1]):(),
                                                     (@params > 2)?(arch    => $params[2]):(),
-                                                   )];
+                                                   )]);
      }
      else {
-        return [Debbugs::Packages::binary_to_source(@params)];
+        return encode_utf8_structure([Debbugs::Packages::binary_to_source(@params)]);
      }
 }
 
@@ -304,7 +305,7 @@ sub source_to_binary {
      my $VERSION = __populate_version(pop);
      my ($self,@params) = @_;
 
-     return [Debbugs::Packages::sourcetobinary(@params)];
+     return encode_utf8_structure([Debbugs::Packages::sourcetobinary(@params)]);
 }
 
 =head2 get_versions
@@ -349,7 +350,7 @@ sub get_versions{
      my $VERSION = __populate_version(pop);
      my ($self,@params) = @_;
 
-     return scalar Debbugs::Packages::get_versions(@params);
+     return encode_utf8_structure(scalar Debbugs::Packages::get_versions(@params));
 }
 
 =head1 VERSION COMPATIBILITY
index d44d0bdea277da4add795748ab9c200cabafc3b1..cf6918aa86136325b46444df25a7bd946421ff13 100644 (file)
@@ -37,7 +37,7 @@ use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
 use base qw(Exporter);
 
 use Params::Validate qw(validate_with :types);
-use Debbugs::Common qw(:util :lock :quit :misc);
+use Debbugs::Common qw(:util :lock :quit :misc :utf8);
 use Debbugs::Config qw(:config);
 use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522);
 use Debbugs::Packages qw(makesourceversions make_source_versions getversions get_versions binary_to_source);
@@ -45,7 +45,7 @@ use Debbugs::Versions;
 use Debbugs::Versions::Dpkg;
 use POSIX qw(ceil);
 use File::Copy qw(copy);
-use Encode qw(decode encode);
+use Encode qw(decode encode is_utf8);
 
 use Storable qw(dclone);
 use List::Util qw(min max);
@@ -217,6 +217,7 @@ sub read_bug{
        }
        return undef;
     }
+    binmode($status_fh,':encoding(UTF-8)');
 
     my %data;
     my @lines;
@@ -240,9 +241,6 @@ sub read_bug{
 
     my %namemap = reverse %fields;
     for my $line (@lines) {
-       eval {
-           $line = decode("utf8",$line,Encode::FB_CROAK);
-       };
         if ($line =~ /(\S+?): (.*)/) {
             my ($name, $value) = (lc $1, $2);
            # this is a bit of a hack; we should never, ever have \r
@@ -253,9 +251,13 @@ sub read_bug{
         }
     }
     for my $field (keys %fields) {
-        $data{$field} = '' unless exists $data{$field};
+       $data{$field} = '' unless exists $data{$field};
+    }
+    if ($version < 3) {
+       for my $field (@rfc1522_fields) {
+           $data{$field} = decode_rfc1522($data{$field});
+       }
     }
-
     $data{severity} = $config{default_severity} if $data{severity} eq '';
     for my $field (qw(found_versions fixed_versions found_date fixed_date)) {
         $data{$field} = [split ' ', $data{$field}];
@@ -268,11 +270,6 @@ sub read_bug{
               @{$data{"${field}_date"}});
     }
 
-    if ($version < 3) {
-       for my $field (@rfc1522_fields) {
-           $data{$field} = decode_rfc1522($data{$field});
-       }
-    }
     my $status_modified = (stat($status))[9];
     # Add log last modified time
     $data{log_modified} = (stat($log))[9];
@@ -596,7 +593,7 @@ version.
 
 sub makestatus {
     my ($data,$version) = @_;
-    $version = 2 unless defined $version;
+    $version = 3 unless defined $version;
 
     my $contents = '';
 
@@ -609,6 +606,8 @@ sub makestatus {
     }
     %newdata = %{join_status_fields(\%newdata)};
 
+    %newdata = encode_utf8_structure(%newdata);
+
     if ($version < 3) {
         for my $field (@rfc1522_fields) {
             $newdata{$field} = encode_rfc1522($newdata{$field});
@@ -645,9 +644,6 @@ sub makestatus {
             }
         }
     }
-    eval {
-       $contents = encode("utf8",$contents,Encode::FB_CROAK);
-    };
     return $contents;
 }
 
@@ -667,15 +663,23 @@ sub writebug {
     my ($ref, $data, $location, $minversion, $disablebughook) = @_;
     my $change;
 
-    my %outputs = (1 => 'status', 2 => 'summary');
+    my %outputs = (1 => 'status', 3 => 'summary');
     for my $version (keys %outputs) {
         next if defined $minversion and $version < $minversion;
         my $status = getbugcomponent($ref, $outputs{$version}, $location);
         die "can't find location for $ref" unless defined $status;
-        open(S,"> $status.new") || die "opening $status.new: $!";
-        print(S makestatus($data, $version)) ||
+       my $sfh;
+       if ($version >= 3) {
+           open $sfh,">","$status.new"  or
+               die "opening $status.new: $!";
+       }
+       else {
+           open $sfh,">","$status.new"  or
+               die "opening $status.new: $!";
+       }
+        print {$sfh} makestatus($data, $version) or
             die "writing $status.new: $!";
-        close(S) || die "closing $status.new: $!";
+        close($sfh) or die "closing $status.new: $!";
         if (-e $status) {
             $change = 'change';
         } else {
index f5ede9d699f9791b086a49eb7f06c69445d52b70..353392013c90e621780d1febc76290e40954d075 100644 (file)
@@ -141,6 +141,7 @@ sub fill_in_template{
         ref(\$param{template}) eq 'GLOB') {
          $tt_type = 'FILE_HANDLE';
          $tt_source = $param{template};
+         binmode($tt_source,":encoding(UTF-8)");
      }
      elsif (ref($param{template}) eq 'SCALAR') {
          $tt_type = 'STRING';
@@ -193,17 +194,23 @@ sub fill_in_template{
      my $tt;
      if ($tt_type eq 'FILE' and
         defined $tt_templates{$tt_source} and
+        ($tt_templates{$tt_source}{mtime} + 60) < time and
         (stat $tt_source)[9] <= $tt_templates{$tt_source}{mtime}
        ) {
          $tt = $tt_templates{$tt_source}{template};
      }
      else {
+        my $passed_source = $tt_source;
+        my $passed_type = $tt_type;
          if ($tt_type eq 'FILE') {
               $tt_templates{$tt_source}{mtime} =
                    (stat $tt_source)[9];
+              $passed_source = IO::File->new($tt_source,'r');
+              binmode($passed_source,":encoding(UTF-8)");
+              $passed_type = 'FILEHANDLE';
          }
-         $tt = Text::Template->new(TYPE => $tt_type,
-                                   SOURCE => $tt_source,
+         $tt = Text::Template->new(TYPE => $passed_type,
+                                   SOURCE => $passed_source,
                                    UNTAINT => 1,
                                   );
          if ($tt_type eq 'FILE') {
index 8ad688f6923b501ea836557574b2a63f881c738a..de4ec76371633cfe8d9760a14a7f6e2142df6250 100755 (executable)
@@ -8,6 +8,9 @@ BEGIN{
     delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
 }
 
+# STDOUT should be using the utf8 io layer
+binmode(STDOUT,':utf8');
+
 use POSIX qw(strftime);
 use MIME::Parser;
 use MIME::Decoder;
@@ -168,6 +171,7 @@ if (defined($msg) and ($msg-1) <= $#records) {
 }
 my @log;
 if ( $mbox ) {
+     binmode(STDOUT,":raw");
      my $date = strftime "%a %b %d %T %Y", localtime;
      if (@records > 1) {
         print $q->header(-type => "text/plain",
@@ -242,6 +246,7 @@ END
 
 else {
      if (defined $att and defined $msg and @records) {
+        binmode(STDOUT,":raw");
          $msg_num++;
          print handle_email_message($records[0]->{text},
                                     ref => $ref,
index 4f4ea800af986fcd38f0c58ba04dd8e121a41a95..d29c3abc7ff56943a7a0a2196be16e5560bda857 100755 (executable)
@@ -18,6 +18,9 @@ BEGIN{
     delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
 }
 
+# STDOUT should be in utf8 mode
+binmode(STDOUT,':utf8');
+
 use POSIX qw(strftime nice);
 
 use Debbugs::Config qw(:globals :text :config);
index c7a071ccff3687ee7fe296d52dd9033353d7fb8d..988e5fe35eb7788407275150db7830bc04ed3b64 100644 (file)
@@ -4,6 +4,8 @@ debbugs (2.4.2~exp2) experimental; urgency=low
   * affects now appends packages by default (closes: #656371). Thanks to
     Andreas Beckmann and Julien Cristau.
   * Fix spacing in owner block (closes: #670411)
+  * Fix double encoding issues (closes: #672432)
+  * Fix encoding in cgi
 
   [Thanks to Arnout Engelen: ]
   * Add Homepage (closes: #670555).
index e40752cc64d9670da654529de72940e462805ea7..8dfd5fb04f9bb673dc2759f6d147efe54d2c511c 100755 (executable)
@@ -28,6 +28,7 @@ use Debbugs::Text qw(:templates);
 use Debbugs::Config qw(:globals :config);
 
 use Debbugs::Control qw(append_action_to_log);
+use Encode qw(encode_utf8);
 
 chdir( "$gSpoolDir" ) || die "chdir spool: $!\n";
 
@@ -733,7 +734,7 @@ my $common_headers='';
 }
 if ($codeletter eq 'U') { # sent to -submitter
     &htmllog("Message", "sent on", $data->{originator}, "$gBug#$ref.");
-    &sendmessage(<<END,[$data->{originator},@resentccs],[@bccs]);
+    my $enc_msg=<<END;
 Subject: $gBug#$ref: $newsubject
 Reply-To: $replyto, $ref-quiet\@$gEmailDomain
 ${orgsender}Resent-To: $data->{originator}
@@ -743,8 +744,11 @@ Resent-Sender: $gMaintainerEmail
 X-$gProject-PR-Message: report $ref
 X-$gProject-PR-Package: $data->{package}
 X-$gProject-PR-Keywords: $data->{keywords}
-${source_pr_header}$fwd
+${source_pr_header}
 END
+    chomp $enc_msg;
+    $enc_msg = encode_utf8($enc_msg).$fwd."\n";
+    &sendmessage($enc_msg,[$data->{originator},@resentccs],[@bccs]);
 } elsif ($codeletter eq 'B') { # Sent to submit
     my $report_followup = $newref ? 'report' : 'followup';
     &htmllog($newref ? "Report" : "Information", "forwarded",
@@ -752,7 +756,7 @@ END
              "<code>$gBug#$ref</code>".
              (length($data->{package})? "; Package <code>".html_escape($data->{package})."</code>" : '').
              ".");
-    &sendmessage(<<END,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
+    my $enc_msg=<<END;
 Subject: $gBug#$ref: $newsubject
 Reply-To: $replyto, $ref\@$gEmailDomain
 Resent-From: $header{'from'}
@@ -763,8 +767,11 @@ Resent-Sender: $gMaintainerEmail
 X-$gProject-PR-Message: $report_followup $ref
 X-$gProject-PR-Package: $data->{package}
 X-$gProject-PR-Keywords: $data->{keywords}
-${source_pr_header}$fwd
+${source_pr_header}
 END
+    chomp $enc_msg;
+    $enc_msg = encode_utf8($enc_msg).$fwd."\n";
+    &sendmessage($enc_msg,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
 } elsif (@resentccs or @bccs) { # Quiet or Maintainer
     # D and F done far earlier; B just done - so this must be M or Q
     # We preserve whichever it was in the Reply-To (possibly adding
@@ -783,7 +790,7 @@ END
                  (length($data->{package}) ? "; Package <code>".html_escape($data->{package})."</code>" : '').
                  ".");
     }
-    &sendmessage(<<END,[@resentccs],[@bccs]);
+    my $enc_msg=<<END;
 Subject: $gBug#$ref: $newsubject
 Reply-To: $replyto, $ref-$baddressroot\@$gEmailDomain
 Resent-From: $header{'from'}
@@ -794,8 +801,11 @@ Resent-Sender: $gMaintainerEmail
 ${common_headers}X-$gProject-PR-Message: $report_followup $ref
 X-$gProject-PR-Package: $data->{package}
 X-$gProject-PR-Keywords: $data->{keywords}
-${source_pr_header}$fwd
+${source_pr_header}
 END
+    chomp $enc_msg;
+    $enc_msg = encode_utf8($enc_msg).$fwd."\n";
+    &sendmessage($enc_msg,[@resentccs],[@bccs]);
 }
 
 my $htmlbreak= length($brokenness) ? "<p>\n".html_escape($brokenness)."\n<p>\n" : '';
index 10bf167ae7410888384d26dc078bdca73a3714b5..d103eb19e445987ac40215102c755ea3d6fa9018 100644 (file)
@@ -6,28 +6,27 @@ use Test::More tests => 6;
 use warnings;
 use strict;
 
-use utf8;
 use Encode;
 
 use_ok('Debbugs::MIME');
 
 # encode_headers testing
 
-my $test_str = <<'END';
+my $test_str = decode_utf8(<<'END');
 Döñ Ärḿßtrøñĝ <don@donarmstrong.com>
 END
 
-my $test_str2 = <<'END';
+my $test_str2 = decode_utf8(<<'END');
  Döñ Ärḿßtrøñĝ <don@donarmstrong.com>
 END
 
-my $test_str3 =<<'END';
+my $test_str3 =decode_utf8(<<'END');
 foo@bar.com (J fö"ø)
 END
 
 # 1: test decode
 ok(Debbugs::MIME::decode_rfc1522(q(=?iso-8859-1?Q?D=F6n_Armstr=F3ng?= <don@donarmstrong.com>)) eq
-  encode_utf8(q(Dön Armstróng <don@donarmstrong.com>)),"decode_rfc1522 decodes and converts to UTF8 properly");
+  decode_utf8(q(Dön Armstróng <don@donarmstrong.com>)),"decode_rfc1522 decodes and converts to UTF8 properly");
 
 
 # 2: test encode
diff --git a/t/02_common.t b/t/02_common.t
new file mode 100644 (file)
index 0000000..d770d3f
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- mode: cperl;-*-
+
+use Test::More tests => 2;
+use Encode qw(decode_utf8);
+
+use_ok('Debbugs::Common');
+is_deeply(Debbugs::Common::encode_utf8_structure(
+          {a => decode_utf8('föö'),
+          b => [map {decode_utf8($_)} qw(blëh bl♥h)],
+         }),
+         {a => 'föö',
+          b => [qw(blëh bl♥h)],
+         },
+        );
index e92bde14ae2cd72f8776cc1c27a9313e36869be3..3d21049c579ef9e3c994ce261fc701e674ac7fac 100644 (file)
@@ -11,6 +11,7 @@ use utf8;
 use UNIVERSAL;
 
 use Debbugs::MIME qw(decode_rfc1522);
+use Encode qw(encode_utf8);
 
 use_ok('Debbugs::Mail');
 
@@ -24,7 +25,7 @@ blah blah blah
 END
 
 # 1: test decode
-ok(decode_rfc1522(Debbugs::Mail::encode_headers($test_str)) eq $test_str);
+ok(decode_rfc1522(Debbugs::Mail::encode_headers($test_str)) eq encode_utf8($test_str));
 
 # XXX Figure out a good way to test the send message bit of
 # Debbugs::Mail
diff --git a/t/13_utf8_mail.t b/t/13_utf8_mail.t
new file mode 100644 (file)
index 0000000..8ada76a
--- /dev/null
@@ -0,0 +1,134 @@
+# -*- mode: cperl;-*-
+# $Id: 05_mail.t,v 1.1 2005/08/17 21:46:17 don Exp $
+
+use Test::More tests => 12;
+
+use warnings;
+use strict;
+
+# Here, we're going to shoot messages through a set of things that can
+# happen.
+
+# First, we're going to send mesages to receive.
+# To do so, we'll first send a message to submit,
+# then send messages to the newly created bugnumber.
+
+use IO::File;
+use File::Temp qw(tempdir);
+use Cwd qw(getcwd);
+use Debbugs::MIME qw(create_mime_message);
+use File::Basename qw(dirname basename);
+# The test functions are placed here to make things easier
+use lib qw(t/lib);
+use DebbugsTest qw(:all);
+use Data::Dumper;
+use Encode qw(decode encode decode_utf8);
+
+# HTTP::Server:::Simple defines a SIG{CHLD} handler that breaks system; undef it here.
+$SIG{CHLD} = sub {};
+my %config;
+eval {
+     %config = create_debbugs_configuration(debug => exists $ENV{DEBUG}?$ENV{DEBUG}:0);
+};
+if ($@) {
+     BAIL_OUT($@);
+}
+
+my $sendmail_dir = $config{sendmail_dir};
+my $spool_dir = $config{spool_dir};
+my $config_dir = $config{config_dir};
+
+END{
+     if ($ENV{DEBUG}) {
+         diag("spool_dir:   $spool_dir\n");
+         diag("config_dir:   $config_dir\n");
+         diag("sendmail_dir: $sendmail_dir\n");
+     }
+}
+
+# We're going to use create mime message to create these messages, and
+# then just send them to receive.
+
+send_message(to=>'submit@bugs.something',
+            headers => [To   => 'submit@bugs.something',
+                        From => 'foo@bugs.something',
+                        Subject => 'Submiting a bug',
+                       ],
+            body => <<EOF,attachments => [{Type=>"text/plain",Charset=>"utf-8",Data=><<EOF2}]) or fail('Unable to send message');
+Package: foo
+Severity: normal
+
+This is a silly bug
+EOF
+This is the silly bug's test ütff8 attachment.
+EOF2
+
+
+
+# now we check to see that we have a bug, and nextnumber has been incremented
+ok(-e "$spool_dir/db-h/01/1.log",'log file created');
+ok(-e "$spool_dir/db-h/01/1.summary",'sumary file created');
+ok(-e "$spool_dir/db-h/01/1.status",'status file created');
+ok(-e "$spool_dir/db-h/01/1.report",'report file created');
+ok(system('sh','-c','[ $(grep "attachment." '.$spool_dir.'/db-h/01/1.log|grep -v "ütff8"|wc -l) -eq 0 ]') == 0,
+   'Everything attachment is escaped properly');
+
+# next, we check to see that (at least) the proper messages have been
+# sent out. 1) ack to submitter 2) mail to maintainer
+
+# This keeps track of the previous size of the sendmail directory
+my $SD_SIZE = 0;
+$SD_SIZE =
+    num_messages_sent($SD_SIZE,2,
+                     $sendmail_dir,
+                     'submit messages appear to have been sent out properly',
+                    );
+
+
+# now send a message to the bug
+
+send_message(to => '1@bugs.something',
+            headers => [To   => '1@bugs.something',
+                        From => 'foo@bugs.something',
+                        Subject => 'Sending a message to a bug',
+                       ],
+            body => <<EOF) or fail('sending message to 1@bugs.someting failed');
+Package: foo
+Severity: normal
+
+This is a silly bug
+EOF
+
+$SD_SIZE =
+    num_messages_sent($SD_SIZE,2,
+                     $sendmail_dir,
+                     '1@bugs.something messages appear to have been sent out properly');
+
+# just check to see that control doesn't explode
+send_message(to => 'control@bugs.something',
+            headers => [To   => 'control@bugs.something',
+                        From => 'foo@bugs.something',
+                        Subject => 'Munging a bug',
+                       ],
+            body => <<EOF) or fail 'message to control@bugs.something failed';
+severity 1 wishlist
+retitle 1 ütff8 title encoding test
+thanks
+EOF
+
+$SD_SIZE =
+   num_messages_sent($SD_SIZE,1,
+                    $sendmail_dir,
+                    'control@bugs.something messages appear to have been sent out properly');
+# now we need to check to make sure the control message was processed without errors
+ok(system('sh','-c','find '.$sendmail_dir.q( -type f | xargs grep -q "Subject: Processed: Munging a bug")) == 0,
+   'control@bugs.something message was parsed without errors');
+# now we need to check to make sure that the control message actually did anything
+# This is an eval because $ENV{DEBBUGS_CONFIG_FILE} isn't set at BEGIN{} time
+eval "use Debbugs::Status qw(read_bug writebug);";
+my $status = read_bug(bug=>1);
+ok($status->{subject} eq decode_utf8('ütff8 title encoding test'),'bug 1 retitled');
+ok($status->{severity} eq 'wishlist','bug 1 wishlisted');
+ok(system('sh','-c','[ $(egrep "retitle.*encoding test" '.$spool_dir.'/db-h/01/1.log|grep -v "=C3=BCt=EF=AC=808"|wc -l) -eq 0 ]') == 0,
+   'Control messages escaped properly');
+
index 4e510f6af3a8bcd3debcba17f800fc8c6894a661..253f1d7acbe61f9d5dedae7cd549299f5560dcf0 100644 (file)
@@ -145,6 +145,9 @@ sub send_message{
                                                     },
                                          body    => {type => SCALAR,
                                                     },
+                                         attachments => {type => ARRAYREF,
+                                                         default => [],
+                                                        },
                                          run_processall =>{type => BOOLEAN,
                                                            default => 1,
                                                           },
@@ -158,7 +161,9 @@ sub send_message{
      my $pid = open3($wfd,$rfd,$rfd,'scripts/receive')
          or die "Unable to start receive: $!";
      print {$wfd} create_mime_message($param{headers},
-                                        $param{body}) or die "Unable to to print to receive";
+                                     $param{body},
+                                     $param{attachments}) or
+                                         die "Unable to to print to receive";
      close($wfd) or die "Unable to close receive";
      my $err = $? >> 8;
      my $childpid = waitpid($pid,0);