[Rt-commit] rt branch, 3.8-forward-message-encoding, updated. 5ede1b1f8c0d22d1b9227feb3f77e0cdf531dfce

Ruslan Zakirov ruz at bestpractical.com
Thu Jul 23 06:26:52 EDT 2009


The branch, 3.8-forward-message-encoding has been updated
       via  5ede1b1f8c0d22d1b9227feb3f77e0cdf531dfce (commit)
       via  919155fdd3b8f3c8abae2036ae281ccf81ec56a4 (commit)
       via  80dab7c0b28b4fb486907358910cf52898c36859 (commit)
       via  ce3bb815ba90417d9e9936f764eb1cc052a9de0d (commit)
       via  40004997c474dadbf03f56e7db8b1141a7ed64b9 (commit)
      from  ab9effd79f0990b0b53d652880db87e9e0223320 (commit)

Summary of changes:
 lib/RT/Action/SendEmail.pm                        |   69 ++---------------
 lib/RT/Attachment_Overlay.pm                      |   59 ++++-----------
 lib/RT/Interface/Email.pm                         |   84 ++++++++++++++++++++-
 sbin/rt-test-dependencies.in                      |    2 +-
 share/html/Ticket/Attachment/WithHeaders/dhandler |   12 ++--
 share/html/Ticket/Attachment/dhandler             |    7 +-
 6 files changed, 114 insertions(+), 119 deletions(-)

- Log -----------------------------------------------------------------
commit 40004997c474dadbf03f56e7db8b1141a7ed64b9
Author: Ruslan Zakirov <Ruslan.Zakirov at gmail.com>
Date:   Thu Jul 23 05:54:23 2009 +0400

    replace OriginalHeaders with EncodedHeaders
    
    There is no such thing as OriginalHeaders, if a field in the head
    contains not-ASCII characters then you have to use Q or B encodings.
    These encodings define character set of the content and can have
    multiple chunks with mixed character sets. We don't store all these
    variations in charsets across fields, so we can not restore original
    headers.
    
    As far as I know OriginalEncoding can not be base64 or quoted printable,
    so some parts of the code has never worked.
    
    For purpose of "Download with headers" feature it's better to have
    EncodedHeaders method that just encodes headers into a character set.

diff --git a/lib/RT/Attachment_Overlay.pm b/lib/RT/Attachment_Overlay.pm
index e1eba64..3892595 100644
--- a/lib/RT/Attachment_Overlay.pm
+++ b/lib/RT/Attachment_Overlay.pm
@@ -320,46 +320,6 @@ sub OriginalContent {
     return $content;
 }
 
-=head2 OriginalHeaders
-
-Returns the attachment's headers as octets before RT's mangling.  Currently,
-this just means restoring text headers back to their original encoding.
-
-=cut
-
-sub OriginalHeaders {
-    my $self = shift;
-
-    return $self->Headers unless RT::I18N::IsTextualContentType($self->ContentType);
-    my $enc = $self->OriginalEncoding;
-
-    my $headers;
-    if ( !$self->ContentEncoding || $self->ContentEncoding eq 'none' ) {
-        $headers = $self->_Value('Headers', decode_utf8 => 0);
-    } elsif ( $self->ContentEncoding eq 'base64' ) {
-        $headers = MIME::Base64::decode_base64($self->_Value('Headers', decode_utf8 => 0));
-    } elsif ( $self->ContentEncoding eq 'quoted-printable' ) {
-        $headers = MIME::QuotedPrint::decode($self->_Value('Headers', decode_utf8 => 0));
-    } else {
-        return( $self->loc("Unknown ContentEncoding [_1]", $self->ContentEncoding));
-    }
-
-    # Turn *off* the SvUTF8 bits here so decode_utf8 and from_to below can work.
-    local $@;
-    Encode::_utf8_off($headers);
-
-    if (!$enc || $enc eq '' ||  $enc eq 'utf8' || $enc eq 'utf-8') {
-        # If we somehow fail to do the decode, at least push out the raw bits
-        eval { return( Encode::decode_utf8($headers)) } || return ($headers);
-    }
-
-    eval { Encode::from_to($headers, 'utf8' => $enc) } if $enc;
-    if ($@) {
-        $RT::Logger->error("Could not convert attachment headers from assumed utf8 to '$enc' :".$@);
-    }
-    return $headers;
-}
-
 =head2 OriginalEncoding
 
 Returns the attachment's original encoding.
@@ -539,6 +499,21 @@ sub Headers {
     return join("\n", $_[0]->SplitHeaders);
 }
 
+=head2 EncodedHeaders
+
+Takes encoding as argument and returns the attachment's headers as octets in encoded
+using the encoding.
+
+This is not protection using quoted printable or base64 encoding.
+
+=cut
+
+sub EncodedHeaders {
+    my $self = shift;
+    my $encoding = shift || 'utf8';
+    return Encode::encode( $encoding, $self->Headers );
+}
+
 =head2 GetHeader $TAG
 
 Returns the value of the header Tag as a string. This bypasses the weeding out

commit ce3bb815ba90417d9e9936f764eb1cc052a9de0d
Author: Ruslan Zakirov <Ruslan.Zakirov at gmail.com>
Date:   Thu Jul 23 06:03:26 2009 +0400

    use Encode to get canonic IANA name of an encoding

diff --git a/sbin/rt-test-dependencies.in b/sbin/rt-test-dependencies.in
index d68fa14..92dfccd 100755
--- a/sbin/rt-test-dependencies.in
+++ b/sbin/rt-test-dependencies.in
@@ -234,7 +234,7 @@ Scalar::Util
 Module::Versions::Report 1.05
 Cache::Simple::TimedExpiry
 Calendar::Simple
-Encode 2.13
+Encode 2.21
 CSS::Squish 0.06
 File::Glob
 Devel::StackTrace 1.19
diff --git a/share/html/Ticket/Attachment/WithHeaders/dhandler b/share/html/Ticket/Attachment/WithHeaders/dhandler
index 43f1de0..399da37 100644
--- a/share/html/Ticket/Attachment/WithHeaders/dhandler
+++ b/share/html/Ticket/Attachment/WithHeaders/dhandler
@@ -60,11 +60,11 @@
     }
 
     my $content_type = 'text/plain';
-    if ( my $enc = $AttachmentObj->OriginalEncoding ) {
-        # normalize Encode.pm convention with IANA ones
-        $enc = 'big5'  if $enc eq 'big5-eten';
-        $enc = 'utf-8' if $enc eq 'utf8';
-        $content_type .= ";charset=$enc";
+    my $enc = $AttachmentObj->OriginalEncoding;
+    if ( $enc ) {
+        my $iana = Encode::find_encoding($enc);
+        $iana = $iana ? $iana->mime_name : $enc;
+        $content_type .= ";charset=$iana";
     }
 
     # XXX: should we check handle html here and integrate headers into html?

commit 80dab7c0b28b4fb486907358910cf52898c36859
Author: Ruslan Zakirov <Ruslan.Zakirov at gmail.com>
Date:   Thu Jul 23 06:07:05 2009 +0400

    use Encode to find IANA name in Ticket/Attachment/dhandler

diff --git a/share/html/Ticket/Attachment/dhandler b/share/html/Ticket/Attachment/dhandler
index 1ad51d9..d4d556b 100755
--- a/share/html/Ticket/Attachment/dhandler
+++ b/share/html/Ticket/Attachment/dhandler
@@ -74,10 +74,9 @@
      }
 
      if (my $enc = $AttachmentObj->OriginalEncoding) {
-	# normalize Encode.pm convention with IANA ones
-        $enc = 'big5'  if $enc eq 'big5-eten';
-        $enc = 'utf-8' if $enc eq 'utf8';
-	$content_type .= ";charset=$enc";
+        my $iana = Encode::find_encoding( $enc );
+        $iana = $iana? $iana->mime_name : $enc;
+	$content_type .= ";charset=$iana";
      }
 
      # unless (RT->Config->Get('TrustMIMEAttachments')) {

commit 919155fdd3b8f3c8abae2036ae281ccf81ec56a4
Author: Ruslan Zakirov <Ruslan.Zakirov at gmail.com>
Date:   Thu Jul 23 07:51:25 2009 +0400

    use EncodedHeaders when show an attachment with headers

diff --git a/share/html/Ticket/Attachment/WithHeaders/dhandler b/share/html/Ticket/Attachment/WithHeaders/dhandler
index 399da37..18c39eb 100644
--- a/share/html/Ticket/Attachment/WithHeaders/dhandler
+++ b/share/html/Ticket/Attachment/WithHeaders/dhandler
@@ -70,7 +70,7 @@
     # XXX: should we check handle html here and integrate headers into html?
     $r->content_type( $content_type );
     $m->clear_buffer;
-    $m->out( $AttachmentObj->OriginalHeaders );
+    $m->out( $AttachmentObj->EncodedHeaders( $enc ) );
     $m->out( "\n\n" );
     $m->out( $AttachmentObj->OriginalContent );
     $m->abort;

commit 5ede1b1f8c0d22d1b9227feb3f77e0cdf531dfce
Author: Ruslan Zakirov <Ruslan.Zakirov at gmail.com>
Date:   Thu Jul 23 14:25:56 2009 +0400

    factor out EncodeToMIME from SendEmail action into RT::Interface::Email

diff --git a/lib/RT/Action/SendEmail.pm b/lib/RT/Action/SendEmail.pm
index 7b756be..a09bd3e 100755
--- a/lib/RT/Action/SendEmail.pm
+++ b/lib/RT/Action/SendEmail.pm
@@ -55,8 +55,6 @@ use warnings;
 
 use base qw(RT::Action);
 
-use MIME::Words qw(encode_mimeword);
-
 use RT::EmailParser;
 use RT::Interface::Email;
 use Email::Address;
@@ -413,10 +411,7 @@ sub AddAttachment {
         Type     => $attach->ContentType,
         Charset  => $attach->OriginalEncoding,
         Data     => $attach->OriginalContent,
-        Filename => defined( $attach->Filename )
-        ? $self->MIMEEncodeString( $attach->Filename,
-            RT->Config->Get('EmailOutputEncoding') )
-        : undef,
+        Filename => $self->MIMEEncodeString( $attach->Filename ),
         'RT-Attachment:' => $self->TicketObj->Id . "/"
             . $self->TransactionObj->Id . "/"
             . $attach->id,
@@ -1075,68 +1070,18 @@ sub SetHeaderAsEncoding {
 
 }
 
-=head2 MIMEEncodeString STRING ENCODING
+=head2 MIMEEncodeString
+
+Takes a perl string and optional encoding pass it over
+L<RT::Interface::Email/EncodeToMIME>.
 
-Takes a string and a possible encoding and returns the string wrapped in MIME goo.
+Basicly encode a string using B encoding according to RFC2047.
 
 =cut
 
 sub MIMEEncodeString {
     my $self  = shift;
-    my $value = shift;
-
-    # using RFC2047 notation, sec 2.
-    # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
-    my $charset  = shift;
-    my $encoding = 'B';
-
-    # An 'encoded-word' may not be more than 75 characters long
-    #
-    # MIME encoding increases 4/3*(number of bytes), and always in multiples
-    # of 4. Thus we have to find the best available value of bytes available
-    # for each chunk.
-    #
-    # First we get the integer max which max*4/3 would fit on space.
-    # Then we find the greater multiple of 3 lower or equal than $max.
-    my $max = int(
-        (   ( 75 - length( '=?' . $charset . '?' . $encoding . '?' . '?=' ) )
-            * 3
-        ) / 4
-    );
-    $max = int( $max / 3 ) * 3;
-
-    chomp $value;
-
-    if ( $max <= 0 ) {
-
-        # gives an error...
-        $RT::Logger->crit("Can't encode! Charset or encoding too big.");
-        return ($value);
-    }
-
-    return ($value) unless $value =~ /[^\x20-\x7e]/;
-
-    $value =~ s/\s+$//;
-
-    # we need perl string to split thing char by char
-    Encode::_utf8_on($value) unless Encode::is_utf8($value);
-
-    my ( $tmp, @chunks ) = ( '', () );
-    while ( length $value ) {
-        my $char = substr( $value, 0, 1, '' );
-        my $octets = Encode::encode( $charset, $char );
-        if ( length($tmp) + length($octets) > $max ) {
-            push @chunks, $tmp;
-            $tmp = '';
-        }
-        $tmp .= $octets;
-    }
-    push @chunks, $tmp if length $tmp;
-
-    # encode an join chuncks
-    $value = join "\n ",
-        map encode_mimeword( $_, $encoding, $charset ), @chunks;
-    return ($value);
+    return RT::Interface::Email::EncodeToMIME( String => $_[0], Charset => $_[1] );
 }
 
 eval "require RT::Action::SendEmail_Vendor";
diff --git a/lib/RT/Attachment_Overlay.pm b/lib/RT/Attachment_Overlay.pm
index 3892595..9c0fcf6 100644
--- a/lib/RT/Attachment_Overlay.pm
+++ b/lib/RT/Attachment_Overlay.pm
@@ -419,9 +419,7 @@ sub ContentAsMIME {
     my $entity = new MIME::Entity;
     foreach my $header ($self->SplitHeaders) {
         my ($h_key, $h_val) = split /:/, $header, 2;
-	# We need to encode header as RFC 2047, to avoid conflict with
-	# OrignalContent Encoding that may break display
-        $entity->head->add( $h_key, Encode::encode('MIME-Header', $h_val ) );
+        $entity->head->add( $h_key, RT::Interface::Email::EncodeToMIME( String => $h_val ) );
     }
 
     use MIME::Body;
diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm
index bd888ee..703338b 100755
--- a/lib/RT/Interface/Email.pm
+++ b/lib/RT/Interface/Email.pm
@@ -622,7 +622,7 @@ sub ForwardTransaction {
         );
     }
 
-    $mail->head->set( $_ => Encode::encode('MIME-Header', $args{ $_ }) )
+    $mail->head->set( $_ => EncodeToMIME( String => $args{$_} ) )
         foreach grep defined $args{$_}, qw(To Cc Bcc);
 
     $mail->attach(
@@ -643,8 +643,8 @@ sub ForwardTransaction {
         $from = $obj->QueueObj->CorrespondAddress
             || RT->Config->Get('CorrespondAddress');
     }
-    $mail->head->set( Subject => Encode::encode('MIME-Header', "Fwd: $subject") );
-    $mail->head->set( From    => Encode::encode('MIME-Header', $from) );
+    $mail->head->set( Subject => EncodeToMIME( String => "Fwd: $subject" ) );
+    $mail->head->set( From    => EncodeToMIME( String => $from ) );
 
     my $status = RT->Config->Get('ForwardFromUser')
         # never sign if we forward from User
@@ -759,6 +759,84 @@ sub SignEncrypt {
     return 1;
 }
 
+use MIME::Words ();
+
+=head2 EncodeToMIME
+
+Takes a hash with a String and a Charset. Returns the string encoded
+according to RFC2047, using B (base64 based) encoding.
+
+String must be a perl string, octets are returned.
+
+If Charset is not provided then $EmailOutputEncoding config option
+is used, or "latin-1" if that is not set.
+
+=cut
+
+sub EncodeToMIME {
+    my $self  = shift;
+    my %args = (
+        String => undef,
+        Charset  => undef,
+        @_
+    );
+    my $value = $args{'String'};
+    return $value unless $value; # 0 is perfect ascii
+    my $charset  = $args{'Charset'} || RT->Config->Get('EmailOutputEncoding');
+    my $encoding = 'B';
+
+    # using RFC2047 notation, sec 2.
+    # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
+
+    # An 'encoded-word' may not be more than 75 characters long
+    #
+    # MIME encoding increases 4/3*(number of bytes), and always in multiples
+    # of 4. Thus we have to find the best available value of bytes available
+    # for each chunk.
+    #
+    # First we get the integer max which max*4/3 would fit on space.
+    # Then we find the greater multiple of 3 lower or equal than $max.
+    my $max = int(
+        (   ( 75 - length( '=?' . $charset . '?' . $encoding . '?' . '?=' ) )
+            * 3
+        ) / 4
+    );
+    $max = int( $max / 3 ) * 3;
+
+    chomp $value;
+
+    if ( $max <= 0 ) {
+
+        # gives an error...
+        $RT::Logger->crit("Can't encode! Charset or encoding too big.");
+        return ($value);
+    }
+
+    return ($value) unless $value =~ /[^\x20-\x7e]/;
+
+    $value =~ s/\s+$//;
+
+    # we need perl string to split thing char by char
+    Encode::_utf8_on($value) unless Encode::is_utf8($value);
+
+    my ( $tmp, @chunks ) = ( '', () );
+    while ( length $value ) {
+        my $char = substr( $value, 0, 1, '' );
+        my $octets = Encode::encode( $charset, $char );
+        if ( length($tmp) + length($octets) > $max ) {
+            push @chunks, $tmp;
+            $tmp = '';
+        }
+        $tmp .= $octets;
+    }
+    push @chunks, $tmp if length $tmp;
+
+    # encode an join chuncks
+    $value = join "\n ",
+        map MIME::Words::encode_mimeword( $_, $encoding, $charset ),
+        @chunks;
+    return ($value);
+}
 
 sub CreateUser {
     my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;

-----------------------------------------------------------------------


More information about the Rt-commit mailing list