[Rt-commit] rt branch, master, updated. rt-4.1.8-287-g9e7241a

Alex Vandiver alexmv at bestpractical.com
Thu May 9 17:56:31 EDT 2013


The branch, master has been updated
       via  9e7241a17e78a6d2b245ddc7e3411d81985a39eb (commit)
       via  31d50251f9c0ea83b98c1e24316a1430dfa65a9b (commit)
       via  f428fe5e8f3dad991aca31a7e7c7ec25f9bc6049 (commit)
       via  d5a3d74fdb50089ea44101afe74665109b086411 (commit)
       via  52c44bb6d4d7e65f45026bec9a837be3419823f7 (commit)
       via  6e1cfa14ec4395388b943486b38a83b5e0fae43e (commit)
       via  6b2e53315d2fa2c66410a725acd4fa0e51bb2849 (commit)
       via  bc30f4742bf18ac28b65ec8ed0d82fbeee600cf2 (commit)
       via  141986480efb94751bb80b8dd288bcb4d7fc6d7f (commit)
       via  6c78d437d02f4fc55547d17cfd0aaa3a2b2e489e (commit)
       via  ff728bc9082c87d3d9448270c6aa85a7fcaa4000 (commit)
       via  fc7cabb0f09f94edce23eb70d0309b4e5a733b08 (commit)
       via  7bcd6c0abcd1f9273efc020df2107baa0c59efda (commit)
      from  9e1d2ce8f47d3eddf4a1e69d26b6a90552100968 (commit)

Summary of changes:
 lib/RT/EmailParser.pm             |  38 ++++++---
 lib/RT/I18N.pm                    | 159 +++++++++++++++++++++---------------
 sbin/rt-test-dependencies.in      |   6 +-
 t/approval/admincc.t              |   1 -
 t/approval/basic.t                |   2 -
 t/mail/dashboards.t               |   2 +-
 t/mail/header-characters.t        |  22 ++---
 t/mail/mime_decoding.t            | 166 +++++++++++++++++++++++++++++---------
 t/mail/multipart.t                |   2 +-
 t/mail/specials-in-encodedwords.t |   2 +-
 t/web/scrips.t                    |   4 +-
 t/web/simple_search.t             |   4 +-
 12 files changed, 267 insertions(+), 141 deletions(-)

- Log -----------------------------------------------------------------
commit 9e7241a17e78a6d2b245ddc7e3411d81985a39eb
Merge: 9e1d2ce 31d5025
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Thu May 9 17:55:09 2013 -0400

    Merge branch '4.2-on-4.0/protect-more-chars-while-decoding-headers'
    
    Conflicts:
    	lib/RT/I18N.pm
    	sbin/rt-test-dependencies.in
    
    t/web/scrips.t failed due to stricter parsing of email addresses when
    forwarding, and was adjusted to provide an email address with a domain.

diff --cc lib/RT/I18N.pm
index cb51a3d,6e7354d..f4592af
--- a/lib/RT/I18N.pm
+++ b/lib/RT/I18N.pm
@@@ -330,74 -387,47 +387,47 @@@ sub _DecodeMIMEWordsToEncoding 
                           $encoded_word
                           ([^=]*)        # trailing
                          /xgcs;
+     return $str unless @list;
+ 
+     # add everything that hasn't matched to the end of the latest
+     # string in array this happen when we have 'key="=?encoded?="; key="plain"'
+     $list[-1] .= substr($str, pos $str);
+ 
+     $str = '';
+     while (@list) {
+         my ($prefix, $charset, $encoding, $enc_str, $trailing) =
+                 splice @list, 0, 5;
+         $charset  = _CanonicalizeCharset($charset);
+         $encoding = lc $encoding;
+ 
+         $trailing =~ s/\s?\t?$//;               # Observed from Outlook Express
+ 
+         if ( $encoding eq 'q' ) {
+             use MIME::QuotedPrint;
 -            $enc_str =~ tr/_/ /;		# Observed from Outlook Express
++            $enc_str =~ tr/_/ /;                # Observed from Outlook Express
+             $enc_str = decode_qp($enc_str);
+         } elsif ( $encoding eq 'b' ) {
+             use MIME::Base64;
+             $enc_str = decode_base64($enc_str);
+         } else {
+             $RT::Logger->warning("Incorrect encoding '$encoding' in '$str', "
+                 ."only Q(uoted-printable) and B(ase64) are supported");
+         }
  
-     if ( @list ) {
-         # add everything that hasn't matched to the end of the latest
-         # string in array this happen when we have 'key="=?encoded?="; key="plain"'
-         $list[-1] .= substr($str, pos $str);
- 
-         $str = "";
-         while (@list) {
-             my ($prefix, $charset, $encoding, $enc_str, $trailing) =
-                     splice @list, 0, 5;
-             $charset  = _CanonicalizeCharset($charset);
-             $encoding = lc $encoding;
- 
-             $trailing =~ s/\s?\t?$//;               # Observed from Outlook Express
- 
-             if ( $encoding eq 'q' ) {
-                 use MIME::QuotedPrint;
-                 $enc_str =~ tr/_/ /;                # Observed from Outlook Express
-                 $enc_str = decode_qp($enc_str);
-             } elsif ( $encoding eq 'b' ) {
-                 use MIME::Base64;
-                 $enc_str = decode_base64($enc_str);
+         # now we have got a decoded subject, try to convert into the encoding
+         if ( $charset ne $to_charset || $charset =~ /^utf-?8(?:-strict)?$/i ) {
+             if ( Encode::find_encoding($charset) ) {
+                 Encode::from_to( $enc_str, $charset, $to_charset );
              } else {
-                 $RT::Logger->warning("Incorrect encoding '$encoding' in '$str', "
-                     ."only Q(uoted-printable) and B(ase64) are supported");
-             }
- 
-             # now we have got a decoded subject, try to convert into the encoding
-             if ( $charset ne $to_charset || $charset =~ /^utf-?8(?:-strict)?$/i ) {
-                 if ( Encode::find_encoding($charset) ) {
-                     Encode::from_to( $enc_str, $charset, $to_charset );
-                 } else {
-                     $RT::Logger->warning("Charset '$charset' is not supported");
-                     $enc_str =~ s/[^[:print:]]/\357\277\275/g;
-                     Encode::from_to( $enc_str, 'UTF-8', $to_charset )
-                         unless $to_charset eq 'utf-8';
-                 }
+                 $RT::Logger->warning("Charset '$charset' is not supported");
+                 $enc_str =~ s/[^[:print:]]/\357\277\275/g;
+                 Encode::from_to( $enc_str, 'UTF-8', $to_charset )
+                     unless $to_charset eq 'utf-8';
              }
- 
-             # XXX TODO: RT doesn't currently do the right thing with mime-encoded headers
-             # We _should_ be preserving them encoded until after parsing is completed and
-             # THEN undo the mime-encoding.
-             #
-             # This routine should be translating the existing mimeencoding to utf8 but leaving
-             # things encoded.
-             #
-             # It's legal for headers to contain mime-encoded commas and semicolons which
-             # should not be treated as address separators. (Encoding == quoting here)
-             #
-             # until this is fixed, we must escape any string containing a comma or semicolon
-             # this is only a bandaid
- 
-             # Some _other_ MUAs encode quotes _already_, and double quotes
-             # confuse us a lot, so only quote it if it isn't quoted
-             # already.
-             $enc_str = qq{"$enc_str"}
-                 if $enc_str =~ /[,;]/
-                 and $enc_str !~ /^".*"$/
-                 and (!$field || $field =~ /^(?:To$|From$|B?Cc$|Content-)/i);
- 
-             $str .= $prefix . $enc_str . $trailing;
          }
+         $str .= $prefix . $enc_str . $trailing;
      }
  
-     # We might have \n without trailing whitespace, which will result in
-     # invalid headers.
-     $str =~ s/\n//g;
- 
      return ($str)
  }
  
diff --cc sbin/rt-test-dependencies.in
index a2fce75,f5547be..8dfab51
--- a/sbin/rt-test-dependencies.in
+++ b/sbin/rt-test-dependencies.in
@@@ -195,55 -185,70 +195,56 @@@ Devel::StackTrace 1.1
  Digest::base
  Digest::MD5 2.27
  Digest::SHA
- Email::Address
 -DBI 1.37
 -Class::ReturnValue 0.40
 -DBIx::SearchBuilder 1.59
 -Text::Template 1.44
++Email::Address 1.897
++Email::Address::List
 +Encode 2.39
 +Errno
 +File::Glob
  File::ShareDir
  File::Spec 0.8
 +File::Temp 0.19
 +HTML::Entities
 +HTML::FormatText
 +HTML::Mason 1.43
 +HTML::Mason::PSGIHandler 0.52
  HTML::Quoted
 +HTML::RewriteAttributes 0.05
  HTML::Scrubber 0.08
  HTML::TreeBuilder
 -HTML::FormatText
 -Log::Dispatch 2.23
 -Sys::Syslog 0.16
 +HTTP::Message 6.0
 +IPC::Run3
 +JSON
 +LWP::Simple
 +List::MoreUtils
  Locale::Maketext 1.06
 +Locale::Maketext::Fuzzy 0.11
  Locale::Maketext::Lexicon 0.32
 -Locale::Maketext::Fuzzy
 -MIME::Entity 5.504
 +Log::Dispatch 2.30
  Mail::Mailer 1.57
- MIME::Entity 5.425
 -Email::Address 1.897
 -Email::Address::List
 -Text::Wrapper 
 -Time::ParseDate
 -Time::HiRes 
 -File::Temp 0.19
 -Text::Quoted 2.02
 -Tree::Simple 1.04
 -UNIVERSAL::require
 -Regexp::Common
 -Scalar::Util
++MIME::Entity 5.504
 +Module::Refresh 0.03
  Module::Versions::Report 1.05
 -Cache::Simple::TimedExpiry
 -Encode 2.39
 -CSS::Squish 0.06
 -File::Glob
 -Devel::StackTrace 1.19
 -Text::Password::Pronounceable
 -Devel::GlobalDestruction
 -List::MoreUtils
  Net::CIDR
 +Plack 0.9971
 +Plack::Handler::Starlet
 +Regexp::Common
  Regexp::Common::net::CIDR
  Regexp::IPv6
 -.
 -
 -$deps{'MASON'} = [ text_to_hash( << '.') ];
 -HTML::Mason 1.43
 -Errno
 -Digest::MD5 2.27
 -CGI::Cookie 1.20
 +Role::Basic 0.12
 +Scalar::Util
  Storable 2.08
 -Apache::Session 1.53
 -XML::RSS 1.05
 +Symbol::Global::Name 0.02
 +Sys::Syslog 0.16
 +Text::Password::Pronounceable
 +Text::Quoted 2.02
 +Text::Template 1.44
  Text::WikiFormat 0.76
 -CSS::Squish 0.06
 -Devel::StackTrace 1.19
 -JSON
 -IPC::Run3
 -.
 -
 -$deps{'PSGI'} = [ text_to_hash( << '.') ];
 -CGI 3.38
 -CGI::PSGI 0.12
 -HTML::Mason::PSGIHandler 0.52
 -Plack 0.9971
 -Plack::Handler::Starlet
 -CGI::Emulate::PSGI
 +Text::Wrapper
 +Time::HiRes
 +Time::ParseDate
 +Tree::Simple 1.04
 +UNIVERSAL::require
 +XML::RSS 1.05
  .
  
  $deps{'MAILGATE'} = [ text_to_hash( << '.') ];
diff --cc t/mail/mime_decoding.t
index c8679d1,b6d371c..87c2141
--- a/t/mail/mime_decoding.t
+++ b/t/mail/mime_decoding.t
@@@ -87,10 -117,10 +117,10 @@@ inline
  diag q{canonicalize mime word encodings like gb2312};
  {
      my $str = qq{Subject: =?gb2312?B?1NrKwL3nuPe12Lmy09CzrN9eX1NpbXBsaWZpZWRfQ05fR0IyMzEyYQ==?=
 -	=?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=};
 +\t=?gb2312?B?dHRhY2hlbWVudCB0ZXN0IGluIENOIHNpbXBsaWZpZWQ=?=};
  
      is(
-         RT::I18N::DecodeMIMEWordsToUTF8($str),
+         RT::I18N::DecodeMIMEWordsToUTF8($str, "Subject"),
          qq{Subject: 在世界各地共有超過_Simplified_CN_GB2312attachement test in CN simplified},
          "right decoding"
      );
diff --cc t/web/scrips.t
index f1aef54,0ff46bf..a3d0ba2
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@@ -85,7 -76,7 +85,7 @@@ sub prepare_code_with_value 
      $m->submit_form(
          form_name => 'ForwardMessage',
          fields    => {
--            To => 'rt-test, rt-to at example.com',
++            To => 'rt-test at example.com, rt-to at example.com',
          },
          button => 'ForwardAndReturn'
      );
@@@ -101,7 -92,7 +101,7 @@@
      $m->submit_form(
          form_name => 'ForwardMessage',
          fields    => {
--            To => 'rt-test, rt-to at example.com',
++            To => 'rt-test at example.com, rt-to at example.com',
          },
          button => 'ForwardAndReturn'
      );
diff --cc t/web/simple_search.t
index 17930c8,0000000..b323f89
mode 100644,000000..100644
--- a/t/web/simple_search.t
+++ b/t/web/simple_search.t
@@@ -1,222 -1,0 +1,224 @@@
 +use strict;
 +use warnings;
 +
 +use RT::Test tests => undef,
 +    config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );';
 +my ($baseurl, $m) = RT::Test->started_ok;
 +my $url = $m->rt_base_url;
 +
 +my $queue = RT::Queue->new($RT::SystemUser);
 +$queue->Create( Name => 'other' );
 +ok( $queue->id, 'created queue other');
 +
 +my $two_words_queue = RT::Test->load_or_create_queue(
 +    Name => 'Two Words',
 +);
 +ok $two_words_queue && $two_words_queue->id, 'loaded or created a queue';
 +
 +
 +{
 +    my $tickets = RT::Tickets->new( RT->SystemUser );
 +    my $active = "( ".join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray())." )";
 +    my $inactive = "( ".join( " OR ", map "Status = '$_'", RT::Queue->InactiveStatusArray())." )";
 +
 +    require RT::Search::Simple;
 +    my $parser = RT::Search::Simple->new(
 +        TicketsObj => $tickets,
 +        Argument   => '',
 +    );
 +    is $parser->QueryToSQL("foo"), "$active AND ( Subject LIKE 'foo' )", "correct parsing";
 +    is $parser->QueryToSQL("1"), "( Id = 1 )", "correct parsing";
 +    is $parser->QueryToSQL("#1"), "( Id = 1 )", "correct parsing";
 +    is $parser->QueryToSQL("'1'"), "$active AND ( Subject LIKE '1' )", "correct parsing";
 +
 +    is $parser->QueryToSQL("foo bar"),
 +        "$active AND ( Subject LIKE 'foo' AND Subject LIKE 'bar' )",
 +        "correct parsing";
 +    is $parser->QueryToSQL("'foo bar'"),
 +        "$active AND ( Subject LIKE 'foo bar' )",
 +        "correct parsing";
 +
 +    is $parser->QueryToSQL("'foo \\' bar'"),
 +        "$active AND ( Subject LIKE 'foo \\' bar' )",
 +        "correct parsing";
 +    is $parser->QueryToSQL('"foo \' bar"'),
 +        "$active AND ( Subject LIKE 'foo \\' bar' )",
 +        "correct parsing";
 +    is $parser->QueryToSQL('"\f\o\o"'),
 +        "$active AND ( Subject LIKE '\\\\f\\\\o\\\\o' )",
 +        "correct parsing";
 +
 +    is $parser->QueryToSQL("General"), "( Queue = 'General' ) AND $active", "correct parsing";
 +    is $parser->QueryToSQL("'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing";
 +    is $parser->QueryToSQL("queue:'Two Words'"), "( Queue = 'Two Words' ) AND $active", "correct parsing";
 +    is $parser->QueryToSQL("subject:'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing";
 +
 +    is $parser->QueryToSQL("me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing";
 +    is $parser->QueryToSQL("'me'"), "$active AND ( Subject LIKE 'me' )", "correct parsing";
 +    is $parser->QueryToSQL("owner:me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing";
 +    is $parser->QueryToSQL("owner:'me'"), "( Owner = 'me' ) AND $active", "correct parsing";
 +    is $parser->QueryToSQL('owner:root at localhost'), "( Owner.EmailAddress = 'root\@localhost' ) AND $active", "Email address as owner";
 +
 +    is $parser->QueryToSQL("resolved me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' )", "correct parsing";
 +    is $parser->QueryToSQL("resolved active me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' OR Status = 'new' OR Status = 'open' OR Status = 'stalled' )", "correct parsing";
 +    is $parser->QueryToSQL("status:active"), $active, "Explicit active search";
 +    is $parser->QueryToSQL("status:'active'"), "( Status = 'active' )", "Quoting active makes it the actual word";
 +    is $parser->QueryToSQL("inactive me"), "( Owner.id = '__CurrentUser__' ) AND $inactive", "correct parsing";
 +
 +    is $parser->QueryToSQL("cf.Foo:bar"), "( 'CF.{Foo}' LIKE 'bar' ) AND $active", "correct parsing of CFs";
 +    is $parser->QueryToSQL(q{cf."don't foo?":'bar n\\' baz'}), qq/( 'CF.{don\\'t foo?}' LIKE 'bar n\\' baz' ) AND $active/, "correct parsing of CFs with quotes";
 +}
 +
 +my $ticket_found_1 = RT::Ticket->new($RT::SystemUser);
 +my $ticket_found_2 = RT::Ticket->new($RT::SystemUser);
 +my $ticket_not_found = RT::Ticket->new($RT::SystemUser);
 +
 +$ticket_found_1->Create(
 +    Subject   => 'base ticket 1'.$$,
 +    Queue     => 'general',
 +    Owner     => 'root',
 +    Requestor => 'customsearch at localhost',
 +    Content   => 'this is base ticket 1',
 +);
 +ok( $ticket_found_1->id, 'created ticket for custom search');
 +
 +
 +$ticket_found_2->Create(
 +    Subject   => 'base ticket 2'.$$,
 +    Queue     => 'general',
 +    Owner     => 'root',
 +    Requestor => 'customsearch at localhost',
 +    Content   => 'this is base ticket 2',
 +);
 +ok( $ticket_found_2->id, 'created ticket for custom search');
 +
 +$ticket_not_found = RT::Ticket->new($RT::SystemUser);
 +$ticket_not_found->Create(
 +    Subject   => 'not found subject' . $$,
 +    Queue     => 'other',
 +    Owner     => 'nobody',
 +    Requestor => 'notfound at localhost',
 +    Content   => 'this is not found content',
 +);
 +ok( $ticket_not_found->id, 'created ticket for custom search');
 +
 +ok($m->login, 'logged in');
 +
 +my @queries = (
 +    'base ticket',            'root',
 +    'customsearch at localhost', 'requestor:customsearch',
 +    'subject:base',           'subject:"base ticket"',
 +    'queue:general',          'owner:root',
 +);
 +
 +for my $q (@queries) {
 +    $m->form_with_fields('q');
 +    $m->field( q => $q );
 +    $m->submit;
 +    $m->content_contains( 'base ticket 1', 'base ticket 1 is found' );
 +    $m->content_contains( 'base ticket 2', 'base ticket 2 is found' );
 +    $m->content_lacks( 'not found subject', 'not found ticket is not found' );
 +}
 +
 +$ticket_not_found->SetStatus('open');
 +is( $ticket_not_found->Status, 'open', 'status of not found ticket is open' );
 + at queries = qw/new status:new/;
 +for my $q (@queries) {
 +    $m->form_with_fields('q');
 +    $m->field( q => $q );
 +    $m->submit;
 +    $m->content_contains( 'base ticket 1', 'base ticket 1 is found' );
 +    $m->content_contains( 'base ticket 2', 'base ticket 2 is found' );
 +    $m->content_lacks( 'not found subject', 'not found ticket is not found' );
 +}
 +
 + at queries = ( 'fulltext:"base ticket 1"', "'base ticket 1'" );
 +for my $q (@queries) {
 +    $m->form_with_fields('q');
 +    $m->field( q => $q );
 +    $m->submit;
 +    $m->content_contains( 'base ticket 1', 'base ticket 1 is found' );
 +    $m->content_lacks( 'base ticket 2',     'base ticket 2 is not found' );
 +    $m->content_lacks( 'not found subject', 'not found ticket is not found' );
 +}
 +
 +# now let's test with ' or "
 +for my $quote ( q{'}, q{"} ) {
 +    my $user = RT::User->new($RT::SystemUser);
 +    is( ref($user), 'RT::User' );
 +    my ( $id, $msg ) = $user->Create(
 +        Name         => qq!foo${quote}bar!,
 +        EmailAddress => qq!foo${quote}bar$$\@example.com !,
 +        Privileged   => 1,
 +    );
 +    ok ($id, "Creating user - " . $msg );
 +
 +    my ( $grantid, $grantmsg ) =
 +      $user->PrincipalObj->GrantRight( Right => 'OwnTicket' );
 +    ok( $grantid, $grantmsg );
 +
 +
 +
 +    my $ticket_quote = RT::Ticket->new($RT::SystemUser);
 +    $ticket_quote->Create(
 +        Subject   => qq!base${quote}ticket $$!,
 +        Queue     => 'general',
 +        Owner     => $user->Name,
-         Requestor => qq!custom${quote}search\@localhost!,
++        ( $quote eq q{'}
++            ? (Requestor => qq!custom${quote}search\@localhost!)
++            : () ),
 +        Content   => qq!this is base${quote}ticket with quote inside!,
 +    );
 +    ok( $ticket_quote->id, 'created ticket with quote for custom search' );
 +
 +    @queries = (
 +        qq!fulltext:base${quote}ticket!,
 +        "base${quote}ticket",
 +        "owner:foo${quote}bar",
 +        "foo${quote}bar",
 +
 +        # email doesn't allow " character
 +        $quote eq q{'}
 +        ? (
 +            "requestor:custom${quote}search\@localhost",
 +            "custom${quote}search\@localhost",
 +          )
 +        : (),
 +    );
 +    for my $q (@queries) {
 +        $m->form_with_fields('q');
 +        $m->field( q => $q );
 +        $m->submit;
 +        my $escape_quote = $quote;
 +        RT::Interface::Web::EscapeHTML(\$escape_quote);
 +        $m->content_contains( "base${escape_quote}ticket",
 +            "base${quote}ticket is found" );
 +    }
 +}
 +
 +# Create a CF
 +{
 +    my $cf = RT::CustomField->new(RT->SystemUser);
 +    ok( $cf->Create(Name => 'Foo', Type => 'Freeform', MaxValues => '1', Queue => 0) );
 +    ok $cf->Id;
 +
 +    $ticket_found_1->AddCustomFieldValue( Field => 'Foo', Value => 'bar' );
 +    $ticket_found_2->AddCustomFieldValue( Field => 'Foo', Value => 'bar' );
 +    $ticket_not_found->AddCustomFieldValue( Field => 'Foo', Value => 'baz' );
 +    is( $ticket_found_1->FirstCustomFieldValue('Foo'), 'bar', 'cf value is ok' );
 +    is( $ticket_found_2->FirstCustomFieldValue('Foo'), 'bar', 'cf value is ok' );
 +    is( $ticket_not_found->FirstCustomFieldValue('Foo'), 'baz', 'cf value is ok' );
 +
 +    @queries = qw/cf.Foo:bar/;
 +    for my $q (@queries) {
 +        $m->form_with_fields('q');
 +        $m->field( q => $q );
 +        $m->submit;
 +        $m->content_contains( 'base ticket 1', 'base ticket 1 is found' );
 +        $m->content_contains( 'base ticket 2', 'base ticket 2 is found' );
 +        $m->content_lacks( 'not found subject', 'not found ticket is not found' );
 +    }
 +}
 +
 +undef $m;
 +done_testing;

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


More information about the Rt-commit mailing list