diff options
author | Ivan Kohler <ivan@freeside.biz> | 2015-07-09 22:18:55 -0700 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2015-07-09 22:18:55 -0700 |
commit | 1c538bfabc2cd31f27067505f0c3d1a46cba6ef0 (patch) | |
tree | 96922ad4459eda1e649327fd391d60c58d454c53 /rt/t/mail | |
parent | 4f5619288413a185e9933088d9dd8c5afbc55dfa (diff) |
RT 4.2.11, ticket#13852
Diffstat (limited to 'rt/t/mail')
31 files changed, 1919 insertions, 372 deletions
diff --git a/rt/t/mail/autogenerated.t b/rt/t/mail/autogenerated.t new file mode 100644 index 000000000..a37c9b12d --- /dev/null +++ b/rt/t/mail/autogenerated.t @@ -0,0 +1,22 @@ +use strict; +use warnings; + +use RT::Test tests => undef; +use Email::Abstract; + +my $msg = Email::Abstract->new(<<'MSG')->cast("MIME::Entity"); +From: somebody@example.com +To: rt@example.com +Precedence: never-bounce +Precedence: bulk +Subject: testing precedence + +I am bulk mail, hear me roar! +MSG + +ok RT::Interface::Email::CheckForAutoGenerated($msg->head), "Is AutoGenerated"; + +$msg->head->delete("Precedence", 1); +ok !RT::Interface::Email::CheckForAutoGenerated($msg->head), "Isn't AutoGenerated"; + +done_testing; diff --git a/rt/t/mail/charsets-outgoing-plaintext.t b/rt/t/mail/charsets-outgoing-plaintext.t new file mode 100644 index 000000000..be576e0bd --- /dev/null +++ b/rt/t/mail/charsets-outgoing-plaintext.t @@ -0,0 +1,315 @@ +use strict; +use warnings; + +use RT::Test tests => 79, text_templates => 1; + +my %string = ( + ru => { + test => "\x{442}\x{435}\x{441}\x{442}", + autoreply => "\x{410}\x{432}\x{442}\x{43e}\x{43e}\x{442}\x{432}\x{435}\x{442}", + support => "\x{43f}\x{43e}\x{434}\x{434}\x{435}\x{440}\x{436}\x{43a}\x{430}", + }, + latin1 => { + test => Encode::decode('latin1', "t\xE9st"), + autoreply => Encode::decode('latin1', "a\xFCtoreply"), + support => Encode::decode('latin1', "supp\xF5rt"), + }, +); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +diag "make sure queue has no subject tag"; +{ + my ($status, $msg) = $queue->SetSubjectTag( undef ); + ok $status, "set subject tag for the queue" or diag "error: $msg"; +} + +diag "set intial simple autoreply template"; +{ + my $template = RT::Template->new( RT->SystemUser ); + $template->Load('Autoreply'); + ok $template->id, "loaded autoreply tempalte"; + + my ($status, $msg) = $template->SetContent( + "Subject: Autreply { \$Ticket->Subject }\n" + ."\n" + ."hi there it's an autoreply.\n" + ."\n" + ); + ok $status, "changed content of the template" + or diag "error: $msg"; +} + +diag "basic test of autoreply"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; +} + +diag "non-ascii Subject with ascii prefix set in the template"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +foreach my $tag_set ( 'ru', 'latin1' ) { + +diag "set non-ascii subject tag for the queue"; +{ + my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} ); + ok $status, "set subject tag for the queue" or diag "error: $msg"; +} + +diag "ascii subject with non-ascii subject tag"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +diag "non-ascii subject with non-ascii subject tag"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +} # subject tag + +diag "return back the empty subject tag"; +{ + my ($status, $msg) = $queue->SetSubjectTag( undef ); + ok $status, "set subject tag for the queue" or diag "error: $msg"; +} + + +foreach my $prefix_set ( 'ru', 'latin1' ) { + +diag "add non-ascii subject prefix in the autoreply template"; +{ + my $template = RT::Template->new( RT->SystemUser ); + $template->Load('Autoreply'); + ok $template->id, "loaded autoreply tempalte"; + + my ($status, $msg) = $template->SetContent( + "Subject: $string{$prefix_set}{autoreply} { \$Ticket->Subject }\n" + ."\n" + ."hi there it's an autoreply.\n" + ."\n" + ); + ok $status, "changed content of the template" or diag "error: $msg"; +} + +diag "ascii subject with non-ascii subject prefix in template"; +{ + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$prefix_set}{autoreply}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +diag "non-ascii subject with non-ascii subject prefix in template"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$prefix_set}{autoreply}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +foreach my $tag_set ( 'ru', 'latin1' ) { +diag "set non-ascii subject tag for the queue"; +{ + my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} ); + ok $status, "set subject tag for the queue" or diag "error: $msg"; +} + +diag "non-ascii subject, non-ascii prefix in template and non-ascii tag"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$prefix_set}{autoreply}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +} # subject tag + +diag "flush subject tag of the queue"; +{ + my ($status, $msg) = $queue->SetSubjectTag( undef ); + ok $status, "set subject tag for the queue" or diag "error: $msg"; +} + +} # prefix set + + +diag "don't change subject via template"; +# clean DB has autoreply that always changes subject in template, +# we should test situation when subject is not changed from template +{ + my $template = RT::Template->new( RT->SystemUser ); + $template->Load('Autoreply'); + ok $template->id, "loaded autoreply tempalte"; + + my ($status, $msg) = $template->SetContent( + "\n" + ."\n" + ."hi there it's an autoreply.\n" + ."\n" + ); + ok $status, "changed content of the template" or diag "error: $msg"; +} + +diag "non-ascii Subject without changes in template"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +foreach my $tag_set ( 'ru', 'latin1' ) { +diag "set non-ascii subject tag for the queue"; +{ + my ($status, $msg) = $queue->SetSubjectTag( $string{$tag_set}{support} ); + ok $status, "set subject tag for the queue" or diag "error: $msg"; +} + +diag "non-ascii Subject without changes in template and with non-ascii subject tag"; +foreach my $set ( 'ru', 'latin1' ) { + my $ticket = RT::Ticket->new( RT->SystemUser ); + $ticket->Create( + Queue => $queue->id, + Subject => $string{$set}{test}, + Requestor => 'root@localhost', + ); + my @mails = RT::Test->fetch_caught_mails; + ok @mails, "got some outgoing emails"; + + my $status = 1; + foreach my $mail ( @mails ) { + my $entity = parse_mail( $mail ); + my $subject = Encode::decode( "UTF-8", $entity->head->get('Subject') ); + $subject =~ /$string{$set}{test}/ + or do { $status = 0; diag "wrong subject: $subject" }; + $subject =~ /$string{$tag_set}{support}/ + or do { $status = 0; diag "wrong subject: $subject" }; + } + ok $status, "all mails have correct data"; +} + +} # subject tag set + diff --git a/rt/t/mail/charsets-outgoing.t b/rt/t/mail/charsets-outgoing.t index 872721325..0f78f0a58 100644 --- a/rt/t/mail/charsets-outgoing.t +++ b/rt/t/mail/charsets-outgoing.t @@ -32,7 +32,7 @@ diag "make sure queue has no subject tag"; diag "set intial simple autoreply template"; { my $template = RT::Template->new( RT->SystemUser ); - $template->Load('Autoreply'); + $template->Load('Autoreply in HTML'); ok $template->id, "loaded autoreply tempalte"; my ($status, $msg) = $template->SetContent( @@ -144,7 +144,7 @@ foreach my $prefix_set ( 'ru', 'latin1' ) { diag "add non-ascii subject prefix in the autoreply template"; { my $template = RT::Template->new( RT->SystemUser ); - $template->Load('Autoreply'); + $template->Load('Autoreply in HTML'); ok $template->id, "loaded autoreply tempalte"; my ($status, $msg) = $template->SetContent( @@ -248,7 +248,7 @@ diag "don't change subject via template"; # we should test situation when subject is not changed from template { my $template = RT::Template->new( RT->SystemUser ); - $template->Load('Autoreply'); + $template->Load('Autoreply in HTML'); ok $template->id, "loaded autoreply tempalte"; my ($status, $msg) = $template->SetContent( diff --git a/rt/t/mail/crypt-gnupg.t b/rt/t/mail/crypt-gnupg.t index ffb059706..567573e93 100644 --- a/rt/t/mail/crypt-gnupg.t +++ b/rt/t/mail/crypt-gnupg.t @@ -10,9 +10,10 @@ BEGIN { qw/data gnupg keyrings/ ); } -use RT::Test::GnuPG tests => 96, gnupg_options => { homedir => $homedir }; +use RT::Test::GnuPG tests => 100, gnupg_options => { homedir => $homedir }; use Test::Warn; +use_ok('RT::Crypt'); use_ok('MIME::Entity'); diag 'only signing. correct passphrase'; @@ -22,10 +23,12 @@ diag 'only signing. correct passphrase'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); ok( $entity, 'signed entity'); ok( !$res{'logger'}, "log is here as well" ) or diag $res{'logger'}; - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 2, 'two records: passphrase, signing'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'good passphrase'); @@ -36,15 +39,17 @@ diag 'only signing. correct passphrase'; ok( $entity->is_multipart, 'signed message is multipart' ); is( $entity->parts, 2, 'two parts' ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'signed', "have signed part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); - my @res = RT::Crypt::GnuPG::VerifyDecrypt( Entity => $entity ); + my @res = RT::Crypt->VerifyDecrypt( Entity => $entity ); is scalar @res, 1, 'one operation'; - @status = RT::Crypt::GnuPG::ParseStatus( $res[0]{'status'} ); + @status = RT::Crypt->ParseStatus( + Protocol => $res[0]{'Protocol'}, Status => $res[0]{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'Verify', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'good passphrase'); @@ -60,7 +65,7 @@ diag 'only signing. missing passphrase'; ); my %res; warning_like { - %res = RT::Crypt::GnuPG::SignEncrypt( + %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => '' @@ -69,7 +74,9 @@ diag 'only signing. missing passphrase'; ok( $res{'exit_code'}, "couldn't sign without passphrase"); ok( $res{'error'} || $res{'logger'}, "error is here" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'MISSING', 'missing passphrase'); @@ -85,7 +92,7 @@ diag 'only signing. wrong passphrase'; my %res; warning_like { - %res = RT::Crypt::GnuPG::SignEncrypt( + %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'wrong', @@ -95,7 +102,9 @@ diag 'only signing. wrong passphrase'; ok( $res{'exit_code'}, "couldn't sign with bad passphrase"); ok( $res{'error'} || $res{'logger'}, "error is here" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'BAD', 'wrong passphrase'); @@ -109,18 +118,20 @@ diag 'encryption only'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, "successful encryption" ); ok( !$res{'logger'}, "no records in logger" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'Encrypt', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'done'); ok($entity, 'get an encrypted part'); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -138,7 +149,7 @@ diag 'encryption only, bad recipient'; my %res; warning_like { - %res = RT::Crypt::GnuPG::SignEncrypt( + %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0, ); @@ -147,7 +158,9 @@ diag 'encryption only, bad recipient'; ok( $res{'exit_code'}, 'no way to encrypt without keys of recipients'); ok( $res{'logger'}, "errors are in logger" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Keyword'}, 'INV_RECP', 'invalid recipient'); } @@ -160,11 +173,13 @@ diag 'encryption and signing with combined method'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Passphrase => 'test' ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Passphrase => 'test' ); ok( !$res{'exit_code'}, "successful encryption with signing" ); ok( !$res{'logger'}, "no records in logger" ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res{'Protocol'}, Status => $res{'status'} + ); is( scalar @status, 3, 'three records: passphrase, sign and encrypt'); is( $status[0]->{'Operation'}, 'PassphraseCheck', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'done'); @@ -175,7 +190,7 @@ diag 'encryption and signing with combined method'; ok($entity, 'get an encrypted and signed part'); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -190,14 +205,14 @@ diag 'encryption and signing with cascading, sign on encrypted'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, 'successful encryption' ); ok( !$res{'logger'}, "no records in logger" ); - %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); + %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); ok( !$res{'exit_code'}, 'successful signing' ); ok( !$res{'logger'}, "no records in logger" ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part, top most' ); is( $parts[0]->{'Type'}, 'signed', "have signed part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -212,7 +227,7 @@ diag 'find signed/encrypted part deep inside'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, "success" ); $entity->make_multipart( 'mixed', Force => 1 ); $entity->attach( @@ -220,7 +235,7 @@ diag 'find signed/encrypted part deep inside'; Data => ['-'x76, 'this is mailing list'], ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 1, 'one protected part' ); is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); @@ -236,7 +251,7 @@ diag 'wrong signed/encrypted parts: no protocol'; Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0, ); @@ -245,11 +260,12 @@ diag 'wrong signed/encrypted parts: no protocol'; $entity->head->mime_attr( 'Content-Type.protocol' => undef ); my @parts; - warning_like { - @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); - } qr{Entity is 'multipart/encrypted', but has no protocol defined. Skipped}; - - is( scalar @parts, 0, 'no protected parts' ); + warning_like { @parts = RT::Crypt->FindProtectedParts( Entity => $entity ) } + qr{Entity is 'multipart/encrypted', but has no protocol defined. Checking for PGP part}; + is( scalar @parts, 1, 'one protected part' ); + is( $parts[0]->{'Type'}, 'encrypted', "have encrypted part" ); + is( $parts[0]->{'Format'}, 'RFC3156', "RFC3156 format" ); + is( $parts[0]->{'Top'}, $entity, "it's the same entity" ); } diag 'wrong signed/encrypted parts: not enought parts'; @@ -261,7 +277,7 @@ diag 'wrong signed/encrypted parts: not enought parts'; Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0, ); @@ -271,7 +287,7 @@ diag 'wrong signed/encrypted parts: not enought parts'; my @parts; warning_like { - @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); } qr/Encrypted or signed entity must has two subparts. Skipped/; is( scalar @parts, 0, 'no protected parts' ); } @@ -284,11 +300,11 @@ diag 'wrong signed/encrypted parts: wrong proto'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Sign => 0 ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Sign => 0 ); ok( !$res{'exit_code'}, 'success' ); $entity->head->mime_attr( 'Content-Type.protocol' => 'application/bad-proto' ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 0, 'no protected parts' ); } @@ -300,11 +316,11 @@ diag 'wrong signed/encrypted parts: wrong proto'; Subject => 'test', Data => ['test'], ); - my %res = RT::Crypt::GnuPG::SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); + my %res = RT::Crypt->SignEncrypt( Entity => $entity, Encrypt => 0, Passphrase => 'test' ); ok( !$res{'exit_code'}, 'success' ); $entity->head->mime_attr( 'Content-Type.protocol' => 'application/bad-proto' ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 0, 'no protected parts' ); } @@ -314,7 +330,7 @@ diag 'verify inline and in attachment signatures'; my $parser = new MIME::Parser; my $entity = $parser->parse( $fh ); - my @parts = RT::Crypt::GnuPG::FindProtectedParts( Entity => $entity ); + my @parts = RT::Crypt->FindProtectedParts( Entity => $entity ); is( scalar @parts, 2, 'two protected parts' ); is( $parts[1]->{'Type'}, 'signed', "have signed part" ); is( $parts[1]->{'Format'}, 'Inline', "inline format" ); @@ -325,8 +341,10 @@ diag 'verify inline and in attachment signatures'; is( $parts[0]->{'Data'}, $entity->parts(1), "data in second part" ); is( $parts[0]->{'Signature'}, $entity->parts(2), "file's signature in third part" ); - my @res = RT::Crypt::GnuPG::VerifyDecrypt( Entity => $entity ); - my @status = RT::Crypt::GnuPG::ParseStatus( $res[0]->{'status'} ); + my @res = RT::Crypt->VerifyDecrypt( Entity => $entity ); + my @status = RT::Crypt->ParseStatus( + Protocol => $res[0]{'Protocol'}, Status => $res[0]{'status'} + ); is( scalar @status, 1, 'one record'); is( $status[0]->{'Operation'}, 'Verify', 'operation is correct'); is( $status[0]->{'Status'}, 'DONE', 'good passphrase'); diff --git a/rt/t/mail/dashboard-chart-with-utf8.t b/rt/t/mail/dashboard-chart-with-utf8.t index 37f8ce0c6..4fe483a1b 100644 --- a/rt/t/mail/dashboard-chart-with-utf8.t +++ b/rt/t/mail/dashboard-chart-with-utf8.t @@ -1,16 +1,10 @@ use strict; use warnings; -BEGIN { - require RT::Test; +use RT::Test tests => undef; - if (eval { require GD }) { - RT::Test->import(tests => 15); - } - else { - RT::Test->import(skip_all => 'GD required.'); - } -} +plan skip_all => 'GD required' + unless GD->require; my $root = RT::Test->load_or_create_user( Name => 'root' ); @@ -29,6 +23,7 @@ $m->submit_form( fields => { SavedSearchDescription => 'chart foo', SavedSearchOwner => 'RT::User-' . $root->id, + ChartStyle => 'bar', }, button => 'SavedSearchSave', ); @@ -88,3 +83,5 @@ if ( my $io = $handle->open('r') ) { } is( $mail_image_data, $image, 'image in mail is the same one in web' ); +undef $m; +done_testing; diff --git a/rt/t/mail/dashboards.t b/rt/t/mail/dashboards.t index 6bf4ba520..d7b1ccc7a 100644 --- a/rt/t/mail/dashboards.t +++ b/rt/t/mail/dashboards.t @@ -101,7 +101,7 @@ sub produces_dashboard_mail_ok { # {{{ my $mail = parse_mail( $mails[0] ); is($mail->head->get('Subject'), $subject); - is($mail->head->get('From'), "root\n"); + is($mail->head->get('From'), qq{"root" <root\@localhost>\n}); is($mail->head->get('Content-Transfer-Encoding'), "base64\n"); is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n"); is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n"); diff --git a/rt/t/mail/digest-attributes.t b/rt/t/mail/digest-attributes.t index 54c1c803f..a0940dbb1 100644 --- a/rt/t/mail/digest-attributes.t +++ b/rt/t/mail/digest-attributes.t @@ -51,48 +51,48 @@ my $everyone = RT::Group->new( RT->SystemUser ); ok( $ret, "Loaded 'everyone' group: $msg" ); ( $ret, $msg ) = $everyone->PrincipalObj->GrantRight( Right => 'CreateTicket', - Object => $testq ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted everyone CreateTicket on testq: $msg" ); # Make user_d an admincc for the queue. ( $ret, $msg ) = $user_d->PrincipalObj->GrantRight( Right => 'AdminQueue', - Object => $testq ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted dduser AdminQueue on testq: $msg" ); ( $ret, $msg ) = $testq->AddWatcher( Type => 'AdminCc', - PrincipalId => $user_d->PrincipalObj->id ); + PrincipalId => $user_d->PrincipalObj->id ); ok( $ret || $msg =~ /already/, "dduser added as a queue watcher: $msg" ); # Give the others queue rights. ( $ret, $msg ) = $user_n->PrincipalObj->GrantRight( Right => 'AdminQueue', - Object => $testq ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted emailnormal right on testq: $msg" ); ( $ret, $msg ) = $user_w->PrincipalObj->GrantRight( Right => 'AdminQueue', - Object => $testq ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted emailweekly right on testq: $msg" ); ( $ret, $msg ) = $user_s->PrincipalObj->GrantRight( Right => 'AdminQueue', - Object => $testq ); + Object => $testq ); ok( $ret || $msg =~ /already has/, "Granted emailsusp right on testq: $msg" ); # Create a ticket with To: Cc: Bcc: fields using our four users. my $id; my $ticket = RT::Ticket->new( RT->SystemUser ); ( $id, $ret, $msg ) = $ticket->Create( Queue => $testq->Name, - Requestor => [ $user_w->Name ], - Subject => 'Test ticket for RT::Extension::EmailDigest', - ); + Requestor => [ $user_w->Name ], + Subject => 'Test ticket for RT::Extension::EmailDigest', + ); ok( $ret, "Ticket $id created: $msg" ); # Make the other users ticket watchers. ( $ret, $msg ) = $ticket->AddWatcher( Type => 'Cc', - PrincipalId => $user_n->PrincipalObj->id ); + PrincipalId => $user_n->PrincipalObj->id ); ok( $ret, "Added user_n as a ticket watcher: $msg" ); ( $ret, $msg ) = $ticket->AddWatcher( Type => 'Cc', - PrincipalId => $user_s->PrincipalObj->id ); + PrincipalId => $user_s->PrincipalObj->id ); ok( $ret, "Added user_s as a ticket watcher: $msg" ); my $obj; ($id, $msg, $obj ) = $ticket->Correspond( - Content => "This is a ticket response for CC action" ); + Content => "This is a ticket response for CC action" ); ok( $ret, "Transaction created: $msg" ); # Get the deferred notifications that should result. Should be two for @@ -113,11 +113,11 @@ while( my $txn = $txns->Next ) { # If the transaction has content... if( $txn->ContentObj ) { - # ...none of the deferred folk should be in the header. - my $headerstr = $txn->ContentObj->Headers; - foreach my $rcpt( @daily_rcpt, @weekly_rcpt, @susp_rcpt ) { - ok( $headerstr !~ /$rcpt/, "Deferred recipient $rcpt not found in header" ); - } + # ...none of the deferred folk should be in the header. + my $headerstr = $txn->ContentObj->Headers; + foreach my $rcpt( @daily_rcpt, @weekly_rcpt, @susp_rcpt ) { + ok( $headerstr !~ /$rcpt/, "Deferred recipient $rcpt not found in header" ); + } } } diff --git a/rt/t/mail/gateway.t b/rt/t/mail/gateway.t index 4f906c89c..89b1b60ab 100644 --- a/rt/t/mail/gateway.t +++ b/rt/t/mail/gateway.t @@ -420,7 +420,7 @@ EOF diag "Testing preservation of binary attachments"; { # Get a binary blob (Best Practical logo) - my $LOGO_FILE = $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; + my $LOGO_FILE = $RT::StaticPath .'/images/bpslogo.png'; # Create a mime entity with an attachment my $entity = MIME::Entity->build( @@ -636,13 +636,15 @@ EOF close (MAIL); is ($? >> 8, 0, "The mail gateway exited normally"); +DBIx::SearchBuilder::Record::Cachable->FlushCache; + $tick = RT::Ticket->new(RT->SystemUser); $tick->Load( $id ); is( $tick->Id, $id, 'load correct ticket'); is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket via email'); -# check that there is no text transactions writen -is( $tick->Transactions->Count, 2, 'no superfluous transactions'); +# check that there is no text transactions writen (create + 2 for take) +is( $tick->Transactions->Count, 3, 'no superfluous transactions'); my $status; ($status, $msg) = $tick->SetOwner( RT->Nobody->Id, 'Force' ); @@ -672,8 +674,8 @@ is( $tick->OwnerObj->EmailAddress, 'root@localhost', 'successfuly take ticket vi my $txns = $tick->Transactions; $txns->Limit( FIELD => 'Type', VALUE => 'Correspond'); $txns->OrderBy( FIELD => 'id', ORDER => 'DESC' ); -# +1 because of auto open -is( $tick->Transactions->Count, 6, 'no superfluous transactions'); +# +2 from owner to nobody, +1 because of auto open, +2 from take, +1 from correspond +is( $tick->Transactions->Count, 9, 'no superfluous transactions'); is( $txns->First->Subject, "[$RT::rtname \#$id] correspondence", 'successfuly add correspond within take via email' ); $m->no_warnings_ok; @@ -695,15 +697,16 @@ $tick = RT::Ticket->new(RT->SystemUser); $tick->Load( $id ); is( $tick->Id, $id, 'load correct ticket'); is( $tick->Status, 'resolved', 'successfuly resolved ticket via email'); -is( $tick->Transactions->Count, 7, 'no superfluous transactions'); +# +1 from resolve +is( $tick->Transactions->Count, 10, 'no superfluous transactions'); use RT::User; my $user = RT::User->new( RT->SystemUser ); my ($uid) = $user->Create( Name => 'ext-mailgate', - EmailAddress => 'ext-mailgate@localhost', - Privileged => 1, - Password => 'qwe123', - ); + EmailAddress => 'ext-mailgate@localhost', + Privileged => 1, + Password => 'qwe123', + ); ok( $uid, 'user created for ext-mailgate tests' ); ok( !$user->HasRight( Right => 'OwnTicket', Object => $queue ), "User can't own ticket" ); @@ -733,7 +736,7 @@ ok( $status, "successfuly granted right: $msg" ); my $ace_id = $status; ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can reply to ticket" ); -$m->next_warning_like(qr/Permission Denied/); +$m->next_warning_like(qr/That user may not own tickets in that queue/); $m->next_warning_like(qr/Could not record email: Ticket not taken/); $m->no_leftover_warnings_ok; @@ -752,7 +755,7 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache; cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" ); is( $tick->Transactions->Count, 3, "one transactions added" ); -$m->next_warning_like(qr/Permission Denied/); +$m->next_warning_like(qr/That user may not own tickets in that queue/); $m->next_warning_like(qr/Could not record email: Ticket not taken/); $m->no_leftover_warnings_ok; @@ -771,7 +774,7 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache; cmp_ok( $tick->Owner, '!=', $user->id, "we didn't change owner" ); is( $tick->Transactions->Count, 3, "no transactions added, user can't take ticket first" ); -$m->next_warning_like(qr/Permission Denied/); +$m->next_warning_like(qr/That user may not own tickets in that queue/); $m->next_warning_like(qr/Could not record email: Ticket not taken/); $m->no_leftover_warnings_ok; @@ -784,14 +787,14 @@ my $acl = RT::ACL->new(RT->SystemUser); $acl->Limit( FIELD => 'RightName', VALUE => 'ReplyToTicket' ); $acl->LimitToObject( $RT::System ); while( my $ace = $acl->Next ) { - $ace->Delete; + $ace->Delete; } ok( !$user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "User can't reply to ticket any more" ); -my $group = RT::Group->new( RT->SystemUser ); -ok( $group->LoadQueueRoleGroup( Queue => $qid, Type=> 'Owner' ), "load queue owners role group" ); +my $group = $queue->RoleGroup( 'Owner' ); +ok( $group->Id, "load queue owners role group" ); $ace = RT::ACE->new( RT->SystemUser ); ($ace_id, $msg) = $group->PrincipalObj->GrantRight( Right => 'ReplyToTicket', Object => $queue ); ok( $ace_id, "Granted queue owners role group with ReplyToTicket right" ); @@ -816,7 +819,8 @@ DBIx::SearchBuilder::Record::Cachable->FlushCache; $tick->Load( $id ); is( $tick->Owner, $user->id, "we changed owner" ); ok( $user->HasRight( Right => 'ReplyToTicket', Object => $tick ), "owner can reply to ticket" ); -is( $tick->Transactions->Count, 5, "transactions added" ); +# +2 from take, +1 from correspond +is( $tick->Transactions->Count, 6, "transactions added" ); $m->no_warnings_ok; diff --git a/rt/t/mail/gnupg-bad.t b/rt/t/mail/gnupg-bad.t index a1c45be05..570501c30 100644 --- a/rt/t/mail/gnupg-bad.t +++ b/rt/t/mail/gnupg-bad.t @@ -10,7 +10,7 @@ use RT::Test::GnuPG ), }; -RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::GnuPG' ); +RT->Config->Set( 'MailPlugins' => 'Auth::MailFrom', 'Auth::Crypt' ); my ($baseurl, $m) = RT::Test->started_ok; diff --git a/rt/t/mail/gnupg-incoming.t b/rt/t/mail/gnupg-incoming.t index 48d2d9b73..54b30d2a3 100644 --- a/rt/t/mail/gnupg-incoming.t +++ b/rt/t/mail/gnupg-incoming.t @@ -28,7 +28,7 @@ ok( $m->login, 'we did log in' ); $m->get( $baseurl.'/Admin/Queues/'); $m->follow_link_ok( {text => 'General'} ); $m->submit_form( form_number => 3, - fields => { CorrespondAddress => 'general@example.com' } ); + fields => { CorrespondAddress => 'general@example.com' } ); $m->content_like(qr/general\@example.com.* - never/, 'has key info.'); ok(my $user = RT::User->new(RT->SystemUser)); @@ -73,6 +73,7 @@ run3( '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', + '--no-permission-warning', ), \"fnord\r\n", \$buf, @@ -115,6 +116,7 @@ run3( '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', + '--no-permission-warning', ), \"clearfnord\r\n", \$buf, @@ -157,6 +159,7 @@ run3( '--default-key' => 'recipient@example.com', '--homedir' => $homedir, '--passphrase' => 'recipient', + '--no-permission-warning', ), \"orzzzzzz\r\n", \$buf, @@ -187,7 +190,7 @@ RT::Test->close_mailgate_ok($mail); 'recorded incoming mail that is encrypted' ); is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', 'recorded incoming mail that is encrypted' ); like( $attach->Content, qr/orz/); @@ -224,7 +227,7 @@ RT::Test->close_mailgate_ok($mail); 'recorded incoming mail that is encrypted' ); is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', 'recorded incoming mail that is encrypted' ); like( $attach->Content, qr/orz/); @@ -242,6 +245,7 @@ run3( '--default-key' => 'rt@example.com', '--homedir' => $homedir, '--passphrase' => 'test', + '--no-permission-warning', ), \"alright\r\n", \$buf, @@ -277,6 +281,7 @@ run3( qw(gpg --batch --no-tty --armor --encrypt), '--recipient' => 'random@localhost', '--homedir' => $homedir, + '--no-permission-warning', ), \"should not be there either\r\n", \$buf, @@ -314,6 +319,7 @@ run3( qw(gpg --batch --no-tty --armor --encrypt), '--recipient' => 'rt@example.com', '--homedir' => $homedir, + '--no-permission-warning', ), \"really should not be there either\r\n", \$buf, diff --git a/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t b/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t new file mode 100644 index 000000000..35cfceddd --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-encrypted-plaintext.t @@ -0,0 +1,27 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', + Encrypt => 1, +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); + diff --git a/rt/t/mail/gnupg-outgoing-plain-plaintext.t b/rt/t/mail/gnupg-outgoing-plain-plaintext.t new file mode 100644 index 000000000..32e7d5d8c --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-plain-plaintext.t @@ -0,0 +1,25 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); diff --git a/rt/t/mail/gnupg-outgoing-signed-plaintext.t b/rt/t/mail/gnupg-outgoing-signed-plaintext.t new file mode 100644 index 000000000..cf46edd52 --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-signed-plaintext.t @@ -0,0 +1,27 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', + Sign => 1, +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); + diff --git a/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t b/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t new file mode 100644 index 000000000..c2753d00b --- /dev/null +++ b/rt/t/mail/gnupg-outgoing-signed_encrypted-plaintext.t @@ -0,0 +1,28 @@ +use strict; +use warnings; + +use RT::Test::GnuPG + tests => 104, + text_templates => 1, + gnupg_options => { + passphrase => 'rt-test', + 'trust-model' => 'always', + }; + +RT::Test->import_gnupg_key('rt-recipient@example.com'); +RT::Test->import_gnupg_key( 'rt-test@example.com', 'public' ); + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'rt-recipient@example.com', + CommentAddress => 'rt-recipient@example.com', + Sign => 1, + Encrypt => 1, +); +ok $queue && $queue->id, 'loaded or created queue'; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +create_and_test_outgoing_emails( $queue, $m ); + diff --git a/rt/t/mail/gnupg-realmail.t b/rt/t/mail/gnupg-realmail.t index 834014ccc..1609cffbb 100644 --- a/rt/t/mail/gnupg-realmail.t +++ b/rt/t/mail/gnupg-realmail.t @@ -62,7 +62,7 @@ sub email_ok { my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "$eid: recorded incoming mail that is encrypted" ); diff --git a/rt/t/mail/gnupg-reverification.t b/rt/t/mail/gnupg-reverification.t index deef1ec24..06c2e0d40 100644 --- a/rt/t/mail/gnupg-reverification.t +++ b/rt/t/mail/gnupg-reverification.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test::GnuPG tests => 232, gnupg_options => { passphrase => 'rt-test' }; +use RT::Test::GnuPG tests => undef, gnupg_options => { passphrase => 'rt-test' }; diag "load Everyone group"; my $everyone; @@ -46,8 +46,7 @@ foreach my $file ( @files ) { is $status >> 8, 0, "$eid: the mail gateway exited normally"; ok $id, "$eid: got id of a newly created ticket - $id"; - like($warnings, qr/Had a problem during decrypting and verifying/); - like($warnings, qr/public key not found/); + like($warnings, qr/Public key '0xD328035D84881F1B' is not available/); my $ticket = RT::Ticket->new( RT->SystemUser ); $ticket->Load( $id ); @@ -62,12 +61,10 @@ foreach my $file ( @files ) { $m->content_like(qr/This is .*ID:$eid/ims, "$eid: content is there and message is decrypted"); $m->next_warning_like(qr/public key not found/); - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); # some mails contain multiple signatures if ($eid == 5 || $eid == 17 || $eid == 18) { $m->next_warning_like(qr/public key not found/); - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); } $m->no_leftover_warnings_ok; @@ -90,3 +87,5 @@ foreach my $id ( @ticket_ids ) { $m->no_warnings_ok; } +undef $m; +done_testing; diff --git a/rt/t/mail/header-characters.t b/rt/t/mail/header-characters.t index 004ba8522..38a04b5df 100644 --- a/rt/t/mail/header-characters.t +++ b/rt/t/mail/header-characters.t @@ -6,14 +6,10 @@ use Test::Warn; my ($baseurl, $m) = RT::Test->started_ok; -diag "Testing non-ASCII in From: header"; -SKIP:{ - skip "Test requires Email::Address 1.893 or later, " - . "you have $Email::Address::VERSION", 3, - if $Email::Address::VERSION < 1.893; - +diag "Testing non-ASCII latin1 in From: header"; +{ my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') ); -From: René@example.com> +From: <René@example.com> Reply-To: =?iso-8859-1?Q?Ren=E9?= <René@example.com> Subject: testing non-ASCII From Content-Type: text/plain; charset=iso-8859-1 @@ -23,24 +19,20 @@ here's some content my ($status, $id); warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) } - [qr/Failed to parse Reply-To:.*, From:/, + [(qr/Unable to parse an email address from/) x 2, qr/Couldn't parse or find sender's address/ ], 'Got parse error for non-ASCII in From'; - is( $status >> 8, 0, "The mail gateway exited normally" ); TODO: { - local $TODO = "Currently don't handle non-ASCII for sender"; - ok( $id, "Created ticket" ); - } + local $TODO = "Currently don't handle non-ASCII for sender"; + is( $status >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "Created ticket" ); + } } -diag "Testing iso-8859-1 encoded non-ASCII in From: header"; -SKIP:{ - skip "Test requires Email::Address 1.893 or later, " - . "you have $Email::Address::VERSION", 3, - if $Email::Address::VERSION < 1.893; - - my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.' ) ); +diag "Testing non-ASCII latin1 in From: header with MIME-word-encoded phrase"; +{ + my $mail = Encode::encode( 'iso-8859-1', Encode::decode( "UTF-8", <<'.') ); From: =?iso-8859-1?Q?Ren=E9?= <René@example.com> Reply-To: =?iso-8859-1?Q?Ren=E9?= <René@example.com> Subject: testing non-ASCII From @@ -51,14 +43,14 @@ here's some content my ($status, $id); warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) } - [qr/Failed to parse Reply-To:.*, From:/, + [(qr/Unable to parse an email address from/) x 2, qr/Couldn't parse or find sender's address/ ], 'Got parse error for iso-8859-1 in From'; - is( $status >> 8, 0, "The mail gateway exited normally" ); TODO: { - local $TODO = "Currently don't handle non-ASCII in sender"; - ok( $id, "Created ticket" ); + local $TODO = "Currently don't handle non-ASCII in sender"; + is( $status >> 8, 0, "The mail gateway exited normally" ); + ok( $id, "Created ticket" ); } } @@ -76,6 +68,6 @@ here's some content warnings_like { ( $status, $id ) = RT::Test->send_via_mailgate($mail) } [qr/Couldn't parse or find sender's address/], 'Got parse error with no sender fields'; - is( $status >> 8, 0, "The mail gateway exited normally" ); + is( $status >> 8, 1, "The mail gateway failed" ); ok( !$id, "No ticket created" ); } diff --git a/rt/t/mail/html-outgoing.t b/rt/t/mail/html-outgoing.t new file mode 100644 index 000000000..a37f52cdd --- /dev/null +++ b/rt/t/mail/html-outgoing.t @@ -0,0 +1,187 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +use RT::Test::Email; +use Test::Warn; + +my $root = RT::User->new(RT->SystemUser); +$root->Load('root'); + +# Set root as admincc +my $q = RT::Queue->new(RT->SystemUser); +$q->Load('General'); +my ($ok, $msg) = $q->AddWatcher( Type => 'AdminCc', PrincipalId => $root->Id ); +ok($ok, "Added root as a watcher on the General queue"); + +# Create a couple users to test notifications +my %users; +for my $user_name (qw(enduser tech)) { + my $user = $users{$user_name} = RT::User->new(RT->SystemUser); + $user->Create( Name => ucfirst($user_name), + Privileged => 1, + EmailAddress => $user_name.'@example.com'); + my ($val, $msg); + ($val, $msg) = $user->PrincipalObj->GrantRight(Object =>$q, Right => $_) + for qw(ModifyTicket OwnTicket ShowTicket); +} + +my $t = RT::Ticket->new(RT->SystemUser); +my ($tid, $ttrans, $tmsg); + +diag "Autoreply and AdminCc (Transaction)"; +mail_ok { + ($tid, $ttrans, $tmsg) = + $t->Create(Subject => "The internet is broken", + Owner => 'Tech', Requestor => 'Enduser', + Queue => 'General'); +} { from => qr/The default queue/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] AutoReply: The internet is broken\E/, + body => parts_regex( + 'trouble ticket regarding \*?The internet is broken\*?', + 'trouble ticket regarding <b>The internet is broken</b>' + ), + 'Content-Type' => qr{multipart}, +},{ from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => parts_regex( + 'Request (\[\d+\])?1(\s*[(<]http://localhost:\d+/Ticket/Display\.html\?id=1[)>])?\s*was acted upon by RT_System', + 'Request <a href="http://localhost:\d+/Ticket/Display\.html\?id=1">1</a> was acted upon by RT_System\.</b>' + ), + 'Content-Type' => qr{multipart}, +}; + +diag "Admin Correspondence and Correspondence"; +mail_ok { + ($ok, $tmsg) = $t->Correspond( + MIMEObj => HTML::Mason::Commands::MakeMIMEEntity( + Body => '<p>This is a test of <b>HTML</b> correspondence.</p>', + Type => 'text/html', + ), + ); +} { from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => parts_regex( + 'Ticket URL: (?:\[\d+\])?http://localhost:\d+/Ticket/Display\.html\?id=1.+?'. + 'This is a test of \*?HTML\*? correspondence\.', + 'Ticket URL: <a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>.+?'. + '<p>This is a test of <b>HTML</b> correspondence\.</p>' + ), + 'Content-Type' => qr{multipart}, +},{ from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => parts_regex( + 'This is a test of \*?HTML\*? correspondence\.', + '<p>This is a test of <b>HTML</b> correspondence\.</p>' + ), + 'Content-Type' => qr{multipart}, +}; + +SKIP: { + skip "Only fails on core HTMLFormatter", 9 + unless RT->Config->Get("HTMLFormatter") eq "core"; + diag "Failing HTML -> Text conversion"; + warnings_like { + my $body = '<table><tr><td><table><tr><td>Foo</td></tr></table></td></tr></table>'; + mail_ok { + ($ok, $tmsg) = $t->Correspond( + MIMEObj => HTML::Mason::Commands::MakeMIMEEntity( + Body => $body, + Type => 'text/html', + ), + ); + } { from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => qr{Ticket URL: <a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>.+?$body}s, + 'Content-Type' => qr{text/html}, # TODO + },{ from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] The internet is broken\E/, + body => qr{$body}, + 'Content-Type' => qr{text/html}, # TODO + }; + } [(qr/uninitialized value/, qr/Failed to downgrade HTML/)x3]; +} + + +diag "Admin Comment in HTML"; +mail_ok { + ($ok, $tmsg) = $t->Comment( + MIMEObj => HTML::Mason::Commands::MakeMIMEEntity( + Body => '<p>Comment test, <em>please!</em></p>', + Type => 'text/html', + ), + ); +} { from => qr/RT System/, + bcc => 'root@localhost', + subject => qr/\Q[example.com #1] [Comment] The internet is broken\E/, + body => parts_regex( + 'This is a comment about (\[\d+\])?ticket.1(\s*[(<]http://localhost:\d+/Ticket/Display\.html\?id=1[)>])?\..+?'. + 'It is not sent to the Requestor\(s\):.+?'. + 'Comment test, _?please!_?', + + '<p>This is a comment about <a href="http://localhost:\d+/Ticket/Display\.html\?id=1">ticket 1</a>\. '. + 'It is not sent to the Requestor\(s\):</p>.+?'. + '<p>Comment test, <em>please!</em></p>', + ), + 'Content-Type' => qr{multipart}, +}; + + +diag "Resolved in HTML templates"; +mail_ok { + ($ok, $tmsg) = $t->SetStatus('resolved'); +} { from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] Resolved: The internet is broken\E/, + body => parts_regex( + 'According to our records, your request has been resolved\.', + '<p>According to our records, your request has been resolved\.', + ), + 'Content-Type' => qr{multipart}, +}; + + +diag "Status changes in HTML"; +my $scrip = RT::Scrip->new(RT->SystemUser); +my ($sval, $smsg) =$scrip->Create( + ScripCondition => 'On Status Change', + ScripAction => 'Notify Requestors', + Template => 'Status Change in HTML', + Queue => $q->Id, + Description => 'Tell requestors about status changes' +); +ok ($sval, $smsg); +ok ($scrip->Id, "Created the scrip"); +ok ($scrip->TemplateObj->Id, "Created the scrip template"); +ok ($scrip->ConditionObj->Id, "Created the scrip condition"); +ok ($scrip->ActionObj->Id, "Created the scrip action"); + +mail_ok { + ($ok, $tmsg) = $t->SetStatus('stalled'); +} { from => qr/RT System/, + to => 'enduser@example.com', + subject => qr/\Q[example.com #1] Status Changed to: stalled\E/, + body => parts_regex( + 'http://localhost:\d+/Ticket/Display\.html\?id=1.+?', + '<a href="(http://localhost:\d+/Ticket/Display\.html\?id=1)">\1</a>' + ), + 'Content-Type' => qr{multipart}, +}; + +done_testing; + +sub parts_regex { + my ($text, $html) = @_; + + my $pattern = 'Content-Type: text/plain.+?' . $text . '.+?' . + 'Content-Type: text/html.+?' . $html; + + return qr/$pattern/s; +} + diff --git a/rt/t/mail/mime_decoding.t b/rt/t/mail/mime_decoding.t index fbf884932..1126f1f84 100644 --- a/rt/t/mail/mime_decoding.t +++ b/rt/t/mail/mime_decoding.t @@ -1,14 +1,23 @@ use strict; use warnings; -use RT::Test nodb => 1, tests => 14; +use RT::Test nodb => 1, tests => undef; +use Test::LongString; +use Test::Warn; use_ok('RT::I18N'); diag q{'=' char in a leading part before an encoded part}; { my $str = 'key="plain"; key="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="'; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + 'key="plain"; key="мой_файл.bin"', + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), 'key="plain"; key="мой_файл.bin"', "right decoding" ); @@ -17,8 +26,15 @@ diag q{'=' char in a leading part before an encoded part}; diag q{not compliant with standards, but MUAs send such field when attachment has non-ascii in name}; { my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="'; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + 'attachment; filename="мой_файл.bin"', + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), 'attachment; filename="мой_файл.bin"', "right decoding" ); @@ -27,8 +43,15 @@ diag q{not compliant with standards, but MUAs send such field when attachment ha diag q{'=' char in a trailing part after an encoded part}; { my $str = 'attachment; filename="=?UTF-8?B?0LzQvtC5X9GE0LDQudC7LmJpbg==?="; some_prop="value"'; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + 'attachment; filename="мой_файл.bin"; some_prop="value"', + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), 'attachment; filename="мой_файл.bin"; some_prop="value"', "right decoding" ); @@ -36,12 +59,9 @@ diag q{'=' char in a trailing part after an encoded part}; diag q{adding quotes around mime words containing specials when word is already quoted}; { - my $str = <<"END"; -Content-Disposition: attachment; filename="=?iso-8859-1?Q?foobar,_?= - =?iso-8859-1?Q?barfoo.docx?=" -END - my $decoded = 'Content-Disposition: attachment; filename="foobar, barfoo.docx"'; - is( RT::I18N::DecodeMIMEWordsToUTF8($str), $decoded, "No added quotes" ); + my $str = 'attachment; filename="=?iso-8859-1?Q?foobar,_?=' . "\n" . '=?iso-8859-1?Q?barfoo.docx?="'; + my $decoded = 'attachment; filename="foobar, barfoo.docx"'; + is( RT::I18N::DecodeMIMEWordsToUTF8($str, 'content-disposition'), $decoded, "No added quotes" ); } diag q{regression test for #5248 from rt3.fsck.com}; @@ -49,7 +69,7 @@ diag q{regression test for #5248 from rt3.fsck.com}; my $str = qq{Subject: =?ISO-8859-1?Q?Re=3A_=5BXXXXXX=23269=5D_=5BComment=5D_Frag?=} . qq{\n =?ISO-8859-1?Q?e_zu_XXXXXX--xxxxxx_/_Xxxxx=FCxxxxxxxxxx?=}; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Subject'), qq{Subject: Re: [XXXXXX#269] [Comment] Frage zu XXXXXX--xxxxxx / Xxxxxüxxxxxxxxxx}, "right decoding" ); @@ -58,9 +78,16 @@ diag q{regression test for #5248 from rt3.fsck.com}; diag q{newline and encoded file name}; { my $str = qq{application/vnd.ms-powerpoint;\n\tname="=?ISO-8859-1?Q?Main_presentation.ppt?="}; + warnings_like { + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"}, + "right decoding" + ); + } [qr/DecodeMIMEWordsTo.*?called without field name/i]; is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - qq{application/vnd.ms-powerpoint;\tname="Main presentation.ppt"}, + RT::I18N::DecodeMIMEWordsToUTF8($str,'content-type'), + qq{application/vnd.ms-powerpoint; name="Main presentation.ppt"}, "right decoding" ); } @@ -97,54 +124,116 @@ 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" ); } - diag q{Whitespace between encoded words should be removed}; { - my $str = "=?utf-8?Q?=E3=82=AD?= =?utf-8?Q?=E3=83=A3?="; - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "キャ", - "whitespace between encoded words is removed", - ); - - $str = "=?utf-8?Q?=E3=82=AD?= \n =?utf-8?Q?=E3=83=A3?="; - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "キャ", - "newlines between encoded words also removed", - ); + warnings_like { + my $str = "=?utf-8?Q?=E3=82=AD?= =?utf-8?Q?=E3=83=A3?="; + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "キャ", + "whitespace between encoded words is removed", + ); + + $str = "=?utf-8?Q?=E3=82=AD?= \n =?utf-8?Q?=E3=83=A3?="; + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "キャ", + "newlines between encoded words also removed", + ); + } [(qr/DecodeMIMEWordsTo.*?called without field name/i) x 2]; } diag q{Multiple octets split across QP hunks are correctly reassembled}; { - # This passes even without explicit code to handle it because utf8 - # is perl's internal string encoding. - my $str = "=?utf-8?Q?=E3?= =?utf-8?Q?=82?= =?utf-8?Q?=AD?="; - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "キ", - "UTF8 character split in three is successfully reassembled", - ); - - # Non-utf8 encodings thus also must be checked - $str = <<EOT; chomp $str; + warnings_like { + # This passes even without explicit code to handle it because utf8 + # is perl's internal string encoding. + my $str = "=?utf-8?Q?=E3?= =?utf-8?Q?=82?= =?utf-8?Q?=AD?="; + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "キ", + "UTF8 character split in three is successfully reassembled", + ); + + # Non-utf8 encodings thus also must be checked + $str = <<EOT; chomp $str; =?gb2312?q?Chinese(gb2312)=20=20=C3=C0=B9=FA=C7=B0=CB=BE=B7=A8=B2=BF=B3?= =?gb2312?q?=A4=C3=E6=BC=FB=C8=F8=B4=EF=C4=B7=BA=F3=B3=C6=C6=E4=D7=B4=CC=AC?= =?gb2312?q?=BA=DC=BA=C3=20=20Chinese=20(gb2312)?= EOT - is( - RT::I18N::DecodeMIMEWordsToUTF8($str), - "Chinese(gb2312) 美国前司法部长面见萨达姆后称其状态很好 Chinese (gb2312)", - "gb2312 character is successfully reassembled", + is( + RT::I18N::DecodeMIMEWordsToUTF8($str), + "Chinese(gb2312) 美国前司法部长面见萨达姆后称其状态很好 Chinese (gb2312)", + "gb2312 character is successfully reassembled", + ); + } [(qr/DecodeMIMEWordsTo.*?called without field name/i) x 2]; +} + +diag "multiple mime words containing special chars already in quotes"; +{ + my $str = q{attachment; filename="=?ISO-2022-JP?B?Mi4bJEIlSyVlITwlOSVqJWohPCU5GyhC?= =?ISO-2022-JP?B?LnBkZg==?="}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="2.ニュースリリース.pdf"}, + "base64" ); + $str = q{attachment; filename="=?UTF-8?Q?2=2E=E3=83=8B=E3=83=A5=E3=83=BC=E3=82=B9=E3=83=AA=E3=83=AA?= =?UTF-8?Q?=E3=83=BC=E3=82=B9=2Epdf?="}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="2.ニュースリリース.pdf"}, + "QP" + ); } + +diag "mime word combined with text in quoted filename property"; +{ + my $str = q{attachment; filename="=?UTF-8?B?Q2VjaSBuJ2VzdCBwYXMgdW5l?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une pipe.pdf"}, + "base64" + ); + + $str = q{attachment; filename="=?UTF-8?B?Q2VjaSBuJ2VzdCBwYXMgdW5lLi4u?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une... pipe.pdf"}, + "base64" + ); + + $str = q{attachment; filename="=?UTF-8?Q?Ceci n'est pas une?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une pipe.pdf"}, + "QP" + ); + + $str = q{attachment; filename="=?UTF-8?Q?Ceci n'est pas une...?= pipe.pdf"}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="Ceci n'est pas une... pipe.pdf"}, + "QP" + ); +} + +diag "quotes in filename"; +{ + my $str = q{attachment; filename="=?UTF-8?B?YSAicXVvdGVkIiBmaWxl?="}; + is_string( + RT::I18N::DecodeMIMEWordsToUTF8($str, 'Content-Disposition'), + q{attachment; filename="a \"quoted\" file"}, + "quoted filename correctly decoded" + ); +} + +done_testing; diff --git a/rt/t/mail/multipart.t b/rt/t/mail/multipart.t index 1c1106421..644305ebd 100644 --- a/rt/t/mail/multipart.t +++ b/rt/t/mail/multipart.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 4; +use RT::Test tests => 4, config => q{Set($CorrespondAddress, 'rt@example.com');}; use RT::Test::Email; my $queue = RT::Test->load_or_create_queue( Name => 'General' ); @@ -37,4 +37,4 @@ is(@msgs,2,"sent 2 emails"); diag("We're skipping any testing of the autoreply"); my $entity = parse_mail($msgs[1]); -is($entity->parts, 0, "only one entity"); +is($entity->parts, 2, "only two parts"); diff --git a/rt/t/mail/one-time-recipients.t b/rt/t/mail/one-time-recipients.t index a9881cded..1bc172d71 100644 --- a/rt/t/mail/one-time-recipients.t +++ b/rt/t/mail/one-time-recipients.t @@ -1,7 +1,9 @@ use strict; use warnings; -use RT::Test tests => 38; +use RT::Test tests => undef; +use RT::Test::Email; +use Test::Warn; my $queue = RT::Test->load_or_create_queue( Name => 'General', @@ -17,191 +19,149 @@ my $user = RT::Test->load_or_create_user( ok $user && $user->id, 'loaded or created user'; diag "Reply to ticket with actor as one time cc"; -{ +warnings_are { my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); - my ($status, undef, $msg) = $ticket->Create( - Queue => $queue->id, - Subject => 'test', - Requestor => 'root@localhost', - ); - ok $status, "created ticket"; - - my @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'root@localhost', 'got mail' - } + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'root@localhost', + ); + ok $status, "created ticket"; + } { To => 'root@localhost' }; RT->Config->Set( NotifyActor => 1 ); - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'root@localhost', 'got mail' - } + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'root@localhost' }; RT->Config->Set( NotifyActor => 0 ); - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok !@mails, "no mail - don't notify actor"; - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - CcMessageTo => 'root@localhost', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('Cc'); - $to =~ s/^\s+|\s+$//; - is $to, 'root@localhost', 'got mail' - } -} + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'root@localhost', + ); + ok $status, "replied to a ticket"; + } { Cc => 'root@localhost' }; +} []; diag "Reply to ticket with requestor squelched"; -{ +warnings_are { my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); - my ($status, undef, $msg) = $ticket->Create( - Queue => $queue->id, - Subject => 'test', - Requestor => 'test@localhost', - ); - ok $status, "created ticket"; - - my @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'test@localhost', + ); + ok $status, "created ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'test@localhost' }; $ticket->SquelchMailTo('test@localhost'); - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok !@mails, "no mail - squelched"; - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - CcMessageTo => 'test@localhost', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('Cc'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } -} + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'test@localhost', + ); + ok $status, "replied to a ticket"; + } { Cc => 'test@localhost' }; +} []; diag "Reply to ticket with requestor squelched"; -{ +warnings_are { my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); - my ($status, undef, $msg) = $ticket->Create( - Queue => $queue->id, - Subject => 'test', - Requestor => 'test@localhost', - ); - ok $status, "created ticket"; - - my @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - SquelchMailTo => ['test@localhost'], - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok !@mails, "no mail - squelched"; - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('To'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } - - ($status, $msg) = $ticket->Correspond( - Content => 'test mail', - CcMessageTo => 'test@localhost', - SquelchMailTo => ['test@localhost'], - ); - ok $status, "replied to a ticket"; - - @mails = RT::Test->fetch_caught_mails; - ok @mails, "got some outgoing emails"; - foreach my $mail ( @mails ) { - my $entity = parse_mail( $mail ); - my $to = $entity->head->get('Cc'); - $to =~ s/^\s+|\s+$//; - is $to, 'test@localhost', 'got mail' - } -} + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'test@localhost', + ); + ok $status, "created ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + SquelchMailTo => ['test@localhost'], + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + } { To => 'test@localhost' }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'test@localhost', + SquelchMailTo => ['test@localhost'], + ); + ok $status, "replied to a ticket"; + } { Cc => 'test@localhost' }; +} []; + +diag "Requestor is an RT address"; +warnings_are { + my $ticket = RT::Ticket->new( RT::CurrentUser->new( $user ) ); + mail_ok { + my ($status, undef, $msg) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + Requestor => 'rt-address@example.com', + ); + ok $status, "created ticket"; + } { To => 'rt-address@example.com' }; + + RT->Config->Set( RTAddressRegexp => qr/^rt-address\@example\.com$/i ); + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + ); + ok $status, "replied to a ticket"; + }; + + mail_ok { + my ($status, $msg) = $ticket->Correspond( + Content => 'test mail', + CcMessageTo => 'rt-address@example.com', + ); + ok $status, "replied to a ticket"; + }; +} []; + +done_testing; diff --git a/rt/t/mail/outlook.t b/rt/t/mail/outlook.t index 752a91fae..8f3b71bc8 100644 --- a/rt/t/mail/outlook.t +++ b/rt/t/mail/outlook.t @@ -15,11 +15,11 @@ X-Mailer: $mailer To: rt\@@{[RT->Config->Get('rtname')]} Subject: outlook basic test Content-Type: multipart/alternative; - boundary="----=_NextPart_000_0004_01CB045C.A5A075D0" +\tboundary="----=_NextPart_000_0004_01CB045C.A5A075D0" ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/plain; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: 7bit here is the content @@ -33,7 +33,7 @@ another line ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/html; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: quoted-printable <html>this is fake</html> @@ -61,16 +61,16 @@ X-Mailer: $mailer To: rt\@@{[RT->Config->Get('rtname')]} Subject: outlook basic test Content-Type: multipart/mixed; - boundary="----=_NextPart_000_000F_01CB045E.5222CB40" +\tboundary="----=_NextPart_000_000F_01CB045E.5222CB40" ------=_NextPart_000_000F_01CB045E.5222CB40 Content-Type: multipart/alternative; - boundary="----=_NextPart_001_0010_01CB045E.5222CB40" +\tboundary="----=_NextPart_001_0010_01CB045E.5222CB40" ------=_NextPart_001_0010_01CB045E.5222CB40 Content-Type: text/plain; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: 7bit foo @@ -84,7 +84,7 @@ baz ------=_NextPart_001_0010_01CB045E.5222CB40 Content-Type: text/html; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: quoted-printable <html>this is fake</html> @@ -93,10 +93,10 @@ Content-Transfer-Encoding: quoted-printable ------=_NextPart_000_000F_01CB045E.5222CB40 Content-Type: text/plain; - name="att.txt" +\tname="att.txt" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; - filename="att.txt" +\tfilename="att.txt" this is the attachment! :)=0A= @@ -210,8 +210,8 @@ John Smith Some Company email\@someco.com EOF - test_email( $text, $content, - ' with base64, no X-Mailer, \n\n are replaced' ); + test_email( $text, $content, + ' with base64, no X-Mailer, \n\n are replaced' ); } @@ -223,11 +223,11 @@ X-Mailer: Mutt To: rt\@@{[RT->Config->Get('rtname')]} Subject: outlook basic test Content-Type: multipart/alternative; - boundary="----=_NextPart_000_0004_01CB045C.A5A075D0" +\tboundary="----=_NextPart_000_0004_01CB045C.A5A075D0" ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/plain; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: 7bit foo @@ -241,7 +241,7 @@ baz ------=_NextPart_000_0004_01CB045C.A5A075D0 Content-Type: text/html; - charset="us-ascii" +\tcharset="us-ascii" Content-Transfer-Encoding: quoted-printable <html>this is fake</html> @@ -382,8 +382,8 @@ This isthesig EOF - test_email( $text, $content, - 'Another sample multipart message with Exchange headers' ); + test_email( $text, $content, + 'Another sample multipart message with Exchange headers' ); } sub test_email { diff --git a/rt/t/mail/sendmail-plaintext.t b/rt/t/mail/sendmail-plaintext.t new file mode 100644 index 000000000..b9eb71951 --- /dev/null +++ b/rt/t/mail/sendmail-plaintext.t @@ -0,0 +1,150 @@ +use strict; +use warnings; + +use RT::Test tests => undef, text_templates => 1; + +use File::Spec (); +use Email::Abstract; + +# We're not testing acls here. +my $everyone = RT::Group->new(RT->SystemUser); +$everyone->LoadSystemInternalGroup('Everyone'); +$everyone->PrincipalObj->GrantRight( Right =>'SuperUser' ); + +# some utils +sub first_txn { return $_[0]->Transactions->First } +sub first_attach { return first_txn($_[0])->Attachments->First } +sub count_attachs { return first_txn($_[0])->Attachments->Count } + +sub mail_in_ticket { + my ($filename) = @_; + my $path = RT::Test::get_relocatable_file($filename, + (File::Spec->updir(), 'data', 'emails')); + my $content = RT::Test->file_content($path); + + RT::Test->clean_caught_mails; + my ($status, $id) = RT::Test->send_via_mailgate( $content ); + ok( !$status, "Fed $filename into mailgate"); + + my $ticket = RT::Ticket->new(RT->SystemUser); + $ticket->Load($id); + ok( $ticket->Id, "Successfully created ticket ".$ticket->Id); + + my @mail = map {Email::Abstract->new($_)->cast('MIME::Entity')} + RT::Test->fetch_caught_mails; + return ($ticket, @mail); +} + +{ + my ($ticket) = mail_in_ticket('multipart-report'); + like( first_txn($ticket)->Content , qr/The original message was received/, "It's the bounce"); +} + +for my $encoding ('ISO-8859-1', 'UTF-8') { + RT->Config->Set( EmailOutputEncoding => $encoding ); + + my ($ticket, @mail) = mail_in_ticket('new-ticket-from-iso-8859-1'); + like (first_txn($ticket)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay"); + + is(@mail, 1); + like( $mail[0]->head->get('Content-Type') , qr/$encoding/, + "Its content type is $encoding" ); + my $message_as_string = $mail[0]->bodyhandle->as_string(); + $message_as_string = Encode::decode($encoding, $message_as_string); + like( $message_as_string , qr/H\x{e5}vard/, + "The message's content contains havard's name in $encoding"); +} + +{ + my ($ticket) = mail_in_ticket('multipart-alternative-with-umlaut'); + like( first_txn($ticket)->Content, qr/causes Error/, + "We recorded the content as containing 'causes error'"); + is( count_attachs($ticket), 3, + "Has three attachments, presumably a text-plain, a text-html and a multipart alternative"); +} + +{ + my ($ticket, @mail) = mail_in_ticket('text-html-with-umlaut'); + like( first_attach($ticket)->Content, qr/causes Error/, + "We recorded the content as containing 'causes error'"); + like( first_attach($ticket)->ContentType , qr/text\/html/, + "We recorded the content as text/html"); + is (count_attachs($ticket), 1, + "Has one attachment, just a text-html"); + + is(@mail, 1); + is( $mail[0]->parts, 0, "generated correspondence mime entity does not have parts"); + is( $mail[0]->head->mime_type , "text/plain", "The mime type is a plain"); +} + +{ + my @InputEncodings = RT->Config->Get('EmailInputEncodings'); + RT->Config->Set( EmailInputEncodings => 'koi8-r', @InputEncodings ); + RT->Config->Set( EmailOutputEncoding => 'koi8-r' ); + + my ($ticket, @mail) = mail_in_ticket('russian-subject-no-content-type'); + like( first_attach($ticket)->ContentType, qr/text\/plain/, + "We recorded the content type right"); + is( count_attachs($ticket), 1, + "Has one attachment, presumably a text-plain"); + is( $ticket->Subject, Encode::decode("UTF-8","тест тест"), + "Recorded the subject right"); + + is(@mail, 1); + is( $mail[0]->head->mime_type , "text/plain", "The only part is text/plain "); + like( $mail[0]->head->get("subject"), qr/\Q=?KOI8-R?B?W2V4YW1wbGUuY29tICM2XSBBdXRvUmVwbHk6INTF09Qg1MXT1A==?=\E/, + "The subject is encoded correctly"); + + RT->Config->Set(EmailInputEncodings => @InputEncodings ); + RT->Config->Set(EmailOutputEncoding => 'utf-8'); +} + +{ + my ($ticket, @mail) = mail_in_ticket('nested-rfc-822'); + is( $ticket->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?"); + like( first_attach($ticket)->ContentType, qr/multipart\/mixed/, + "We recorded the content type right"); + is( count_attachs($ticket), 5, + "Has five attachments, presumably a text-plain and a message RFC 822 and another plain"); + + is(@mail, 1); + is( $mail[0]->head->mime_type , "text/plain", "The outgoing mail is plain text"); + + my $encoded_subject = $mail[0]->head->get("Subject"); + chomp $encoded_subject; + my $subject = Encode::decode('MIME-Header',$encoded_subject); + like($subject, qr/Niv\x{e5}er/, "The subject matches the word - $subject"); +} + +{ + my ($ticket) = mail_in_ticket('notes-uuencoded'); + like( first_txn($ticket)->Content, qr/from Lotus Notes/, + "We recorded the content right"); + is( count_attachs($ticket), 3, "Has three attachments"); +} + +{ + my ($ticket) = mail_in_ticket('crashes-file-based-parser'); + like( first_txn($ticket)->Content, qr/FYI/, "We recorded the content right"); + is( count_attachs($ticket), 5, "Has five attachments"); +} + +{ + my ($ticket) = mail_in_ticket('rt-send-cc'); + my $cc = first_attach($ticket)->GetHeader('RT-Send-Cc'); + like ($cc, qr/test$_/, "Found test $_") for 1..5; +} + +{ + diag "Regression test for #5248 from rt3.fsck.com"; + my ($ticket) = mail_in_ticket('subject-with-folding-ws'); + is ($ticket->Subject, 'test', 'correct subject'); +} + +{ + diag "Regression test for #5248 from rt3.fsck.com"; + my ($ticket) = mail_in_ticket('very-long-subject'); + is ($ticket->Subject, '0123456789'x20, 'correct subject'); +} + +done_testing; diff --git a/rt/t/mail/sendmail.t b/rt/t/mail/sendmail.t index 56202ad5d..4ef320611 100644 --- a/rt/t/mail/sendmail.t +++ b/rt/t/mail/sendmail.t @@ -24,7 +24,7 @@ sub mail_in_ticket { RT::Test->clean_caught_mails; my ($status, $id) = RT::Test->send_via_mailgate( $content ); - ok( $status, "Fed $filename into mailgate"); + ok( !$status, "Fed $filename into mailgate"); my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Load($id); @@ -47,12 +47,30 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { like (first_txn($ticket)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay"); is(@mail, 1); - like( $mail[0]->head->get('Content-Type') , qr/$encoding/, - "Its content type is $encoding" ); - my $message_as_string = $mail[0]->bodyhandle->as_string(); + like( $mail[0]->head->get('Content-Type'), qr/multipart\/alternative/, + "Its content type is multipart/alternative" ); + + # The text/html part is guaranteed to not have had non-latin-1 + # characters introduced by the HTML-to-text conversion, so it is + # guaranteed to be able to be represented in latin-1 + like( $mail[0]->parts(1)->head->get('Content-Type'), qr/text\/html.+?$encoding/, + "Second part's content type is text/html $encoding" ); + my $message_as_string = $mail[0]->parts(1)->bodyhandle->as_string(); $message_as_string = Encode::decode($encoding, $message_as_string); like( $message_as_string , qr/H\x{e5}vard/, "The message's content contains havard's name in $encoding"); + + # The text/plain part may have utf-8 characters in it. Accept either encoding. + like( $mail[0]->parts(0)->head->get('Content-Type'), qr/text\/plain.+?(ISO-8859-1|UTF-8)/i, + "First part's content type is text/plain (ISO-8859-1 or UTF-8)" ); + + # Make sure it checks out in whatever encoding it ended up in + $mail[0]->parts(0)->head->get('Content-Type') =~ /text\/plain.+?(ISO-8859-1|UTF-8)/i; + my $found = $1 || $encoding; + $message_as_string = $mail[0]->parts(0)->bodyhandle->as_string(); + $message_as_string = Encode::decode($found, $message_as_string); + like( $message_as_string , qr/H\x{e5}vard/, + "The message's content contains havard's name in $encoding"); } { @@ -73,8 +91,9 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { "Has one attachment, just a text-html"); is(@mail, 1); - is( $mail[0]->parts, 0, "generated correspondence mime entity does not have parts"); - is( $mail[0]->head->mime_type , "text/plain", "The mime type is a plain"); + is( $mail[0]->parts, 2, "generated correspondence mime entity has parts"); + is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part mime type is a plain"); + is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part mime type is an html"); } { @@ -91,7 +110,10 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { "Recorded the subject right"); is(@mail, 1); - is( $mail[0]->head->mime_type , "text/plain", "The only part is text/plain "); + is( $mail[0]->head->mime_type , "multipart/alternative", "The top part is multipart/alternative"); + is( $mail[0]->parts, 2, "generated correspondnece mime entity has parts"); + is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part is a plain"); + is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part is an html"); like( $mail[0]->head->get("subject"), qr/\Q=?KOI8-R?B?W2V4YW1wbGUuY29tICM2XSBBdXRvUmVwbHk6INTF09Qg1MXT1A==?=\E/, "The subject is encoded correctly"); @@ -108,7 +130,10 @@ for my $encoding ('ISO-8859-1', 'UTF-8') { "Has five attachments, presumably a text-plain and a message RFC 822 and another plain"); is(@mail, 1); - is( $mail[0]->head->mime_type , "text/plain", "The outgoing mail is plain text"); + is( $mail[0]->head->mime_type , "multipart/alternative", "The top part is multipart/alternative"); + is( $mail[0]->parts, 2, "generated correspondnece mime entity has parts"); + is( $mail[0]->parts(0)->head->mime_type , "text/plain", "The first part is a plain"); + is( $mail[0]->parts(1)->head->mime_type , "text/html", "The second part is an html"); my $encoded_subject = $mail[0]->head->get("Subject"); chomp $encoded_subject; diff --git a/rt/t/mail/smime/incoming.t b/rt/t/mail/smime/incoming.t new file mode 100644 index 000000000..918844a88 --- /dev/null +++ b/rt/t/mail/smime/incoming.t @@ -0,0 +1,202 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef, actual_server => 1; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; +use Test::Warn; + +my ($url, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +# configure key for General queue +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root@example.com', + EmailAddress => 'root@example.com', +); +RT::Test::SMIME->import_key('root@example.com.crt', $user); +RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System ); + +my $mail = RT::Test->open_mailgate_ok($url); +print $mail <<EOF; +From: root\@localhost +To: rt\@$RT::rtname +Subject: This is a test of new ticket creation as root + +Blah! +Foob! +EOF +RT::Test->close_mailgate_ok($mail); + +{ + my $tick = RT::Test->last_ticket; + is( $tick->Subject, + 'This is a test of new ticket creation as root', + "Created the ticket" + ); + my $txn = $tick->Transactions->First; + like( + $txn->Attachments->First->Headers, + qr/^X-RT-Incoming-Encryption: Not encrypted/m, + 'recorded incoming mail that is not encrypted' + ); + like( $txn->Attachments->First->Content, qr'Blah'); +} + +{ + # test for encrypted mail + my $buf = ''; + run3( + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted message for queue", + $test->key_path('sender@example.com.crt'), + ), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + is ($status >> 8, 0, "The mail gateway exited normally"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + is( $tick->Subject, 'Encrypted message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + is( $msg->GetHeader('X-RT-Privacy'), + 'SMIME', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orz'); + + is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message'); +} + +{ + my $buf = ''; + + run3( + join( + ' ', + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -nodetach -passin pass:123456), + -signer => $test->key_path('root@example.com.crt'), + -inkey => $test->key_path('root@example.com.key'), + ), + '|', + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted and signed message for queue", + $test->key_path('sender@example.com.crt'), + )), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Encrypted and signed message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orzzzz'); +} + +{ + my $buf = ''; + + run3( + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -passin pass:123456), + -signer => $test->key_path('root@example.com.crt'), + -inkey => $test->key_path('root@example.com.key'), + ), + \"Content-type: text/plain\n\nThis is the body", + \$buf, + \*STDERR + ); + $buf = "Subject: Signed email\n" + . "From: root\@example.com\n" + . $buf; + + { + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Signed email', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was signed" + ); + like( $attach->Content, qr/This is the body/ ); + } + + # Make the signature not match + $buf =~ s/This is the body/This is not the body/; + + warning_like { + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Signed email', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + isnt( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was not marked signed" + ); + like( $attach->Content, qr/This is not the body/ ); + } qr/Failure during SMIME verify: The signature did not verify/; + +} + +undef $m; +done_testing; diff --git a/rt/t/mail/smime/other-signed.t b/rt/t/mail/smime/other-signed.t new file mode 100644 index 000000000..4e97e711f --- /dev/null +++ b/rt/t/mail/smime/other-signed.t @@ -0,0 +1,135 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; +use Test::Warn; + +# configure key for General queue +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root@example.com', + EmailAddress => 'root@example.com', +); +RT::Test::SMIME->import_key('root@example.com.crt', $user); +RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System ); + +my $buf = ''; + +run3( + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -passin pass:123456), + -signer => $test->key_path('root@example.com.crt'), + -inkey => $test->key_path('root@example.com.key'), + ), + \"Content-type: text/plain\n\nThis is the body", + \$buf, + \*STDERR +); +$buf = "Subject: Signed email\n" + . "From: root\@example.com\n" + . $buf; + +my $send_mail = sub { + my %args = ( CAPath => undef, AcceptUntrustedCAs => undef, @_ ); + + RT->Config->Get('SMIME')->{$_} = $args{$_} for keys %args; + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Signed email', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + + ($status) = RT::Crypt->ParseStatus( + Protocol => 'SMIME', + Status => $msg->GetHeader('X-RT-SMIME-Status') + ); + + return ($msg, $status); +}; + +# Test with no CA path; should not be marked as signed +warning_like { + my ($msg, $status) = $send_mail->( CAPath => undef ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + undef, + "Message was not marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "BAD", "Verify was a failure"); + is($status->{Trust}, "NONE", "Noted the no trust level"); + like($status->{Message}, qr/not trusted/, "Verify was a failure"); +} qr/Failure during SMIME verify: The signing CA was not trusted/; + +# Test with the correct CA path; marked as signed, trusted +{ + my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/demoCA/cacert.pem" ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', "Message is signed" ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "DONE", "Verify was a success"); + is($status->{Trust}, "FULL", "Noted the full trust level"); +} + +# Test with the other CA +warning_like { + my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/otherCA/cacert.pem" ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + undef, + "Message was not marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "BAD", "Verify was a failure"); + is($status->{Trust}, "NONE", "Noted the no trust level"); + like($status->{Message}, qr/not trusted/, "Verify was a failure"); +} qr/Failure during SMIME verify: The signing CA was not trusted/; + +# Other CA, but allow all CAs +{ + my ($msg, $status) = $send_mail->( CAPath => $test->key_path . "/otherCA/cacert.pem", AcceptUntrustedCAs => 1 ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "DONE", "Verify was a success"); + is($status->{Trust}, "NONE", "Noted the no trust level"); +} + +# No CA path, but allow all CAs +{ + my ($msg, $status) = $send_mail->( CAPath => undef, AcceptUntrustedCAs => 1 ); + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"Enoch Root" <root@example.com>', + "Message was marked as signed" + ); + + is($status->{Operation}, "Verify", "Found the Verify operation"); + is($status->{Status}, "DONE", "Verify was a success"); + is($status->{Trust}, "UNKNOWN", "Noted the no trust level"); +} + +done_testing; diff --git a/rt/t/mail/smime/outgoing.t b/rt/t/mail/smime/outgoing.t new file mode 100644 index 000000000..6f6b00d68 --- /dev/null +++ b/rt/t/mail/smime/outgoing.t @@ -0,0 +1,80 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use RT::Interface::Email; + +my ($url, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +{ + my ($status, $msg) = $queue->SetEncrypt(1); + ok $status, "turn on encyption by default" + or diag "error: $msg"; +} + +my $user; +{ + $user = RT::User->new($RT::SystemUser); + ok($user->LoadByEmail('root@localhost'), "Loaded user 'root'"); + ok($user->Load('root'), "Loaded user 'root'"); + is($user->EmailAddress, 'root@localhost'); + + RT::Test::SMIME->import_key( 'root@example.com.crt' => $user ); +} + +RT::Test->clean_caught_mails; + +{ + my $mail = <<END; +From: root\@localhost +To: rt\@example.com +Subject: This is a test of new ticket creation as an unknown user + +Blah! +Foob! + +END + + my ($status, $id) = RT::Test->send_via_mailgate( + $mail, queue => $queue->Name, + ); + is $status >> 8, 0, "successfuly executed mailgate"; + + my $ticket = RT::Ticket->new($RT::SystemUser); + $ticket->Load( $id ); + ok ($ticket->id, "found ticket ". $ticket->id); +} + +{ + my @mails = RT::Test->fetch_caught_mails; + is scalar @mails, 1, "autoreply"; + + my ($buf, $err); + local $@; + ok(eval { + run3([ + qw(openssl smime -decrypt -passin pass:123456), + '-inkey', $test->key_path('root@example.com.key'), + '-recip', $test->key_path('root@example.com.crt') + ], \$mails[0], \$buf, \$err ) + }, 'can decrypt' + ); + diag $@ if $@; + diag $err if $err; + diag "Error code: $?" if $?; + like($buf, qr'This message has been automatically generated in response'); +} + +undef $m; +done_testing; diff --git a/rt/t/mail/smime/realmail.t b/rt/t/mail/smime/realmail.t new file mode 100644 index 000000000..be157aaee --- /dev/null +++ b/rt/t/mail/smime/realmail.t @@ -0,0 +1,125 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +use Digest::MD5 qw(md5_hex); + +my $test = 'RT::Test::SMIME'; +my $mails = $test->mail_set_path; + +RT->Config->Get('SMIME')->{AcceptUntrustedCAs} = 1; + +RT::Test::SMIME->import_key('root@example.com'); +RT::Test::SMIME->import_key('sender@example.com'); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'we did log in'; +$m->get_ok( '/Admin/Queues/'); +$m->follow_link_ok( {text => 'General'} ); +$m->submit_form( form_number => 3, + fields => { CorrespondAddress => 'root@example.com' } ); + +diag "load Everyone group" if $ENV{'TEST_VERBOSE'}; +my $everyone; +{ + $everyone = RT::Group->new( $RT::SystemUser ); + $everyone->LoadSystemInternalGroup('Everyone'); + ok $everyone->id, "loaded 'everyone' group"; +} + +RT::Test->set_rights( + Principal => $everyone, + Right => ['CreateTicket'], +); + + +my $eid = 0; +for my $usage (qw/signed encrypted signed&encrypted/) { + for my $attachment (qw/plain text-attachment binary-attachment/) { + ++$eid; + diag "Email $eid: $usage, $attachment email" if $ENV{TEST_VERBOSE}; + eval { email_ok($eid, $usage, $attachment) }; + } +} + +undef $m; +done_testing; + +sub email_ok { + my ($eid, $usage, $attachment) = @_; + diag "email_ok $eid: $usage, $attachment" if $ENV{'TEST_VERBOSE'}; + + my ($file) = glob("$mails/$eid-*"); + my $mail = RT::Test->file_content($file); + + my ($status, $id) = RT::Test->send_via_mailgate($mail); + is ($status >> 8, 0, "$eid: The mail gateway exited normally"); + ok ($id, "$eid: got id of a newly created ticket - $id"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $id ); + ok ($tick->id, "$eid: loaded ticket #$id"); + + is ($tick->Subject, + "Test Email ID:$eid", + "$eid: Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is( $msg->GetHeader('X-RT-Privacy'), + 'SMIME', + "$eid: recorded incoming mail that is secured" + ); + + if ($usage =~ /encrypted/) { + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + "$eid: recorded incoming mail that is encrypted" + ); + like( $attachments[0]->Content, qr/ID:$eid/, + "$eid: incoming mail did NOT have original body" + ); + } + else { + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Not encrypted', + "$eid: recorded incoming mail that is not encrypted" + ); + like( $msg->Content || $attachments[0]->Content, qr/ID:$eid/, + "$eid: got original content" + ); + } + + if ($usage =~ /signed/) { + is( $msg->GetHeader('X-RT-Incoming-Signature'), + '"sender" <sender@example.com>', + "$eid: recorded incoming mail that is signed" + ); + } + else { + is( $msg->GetHeader('X-RT-Incoming-Signature'), + undef, + "$eid: recorded incoming mail that is not signed" + ); + } + + if ($attachment =~ /attachment/) { + my ($a) = grep $_->Filename, @attachments; + ok ($a && $a->Id, "$eid: found attachment with filename"); + + my $acontent = $a->Content; + if ($attachment =~ /binary/) + { + is(md5_hex($acontent), '1e35f1aa90c98ca2bab85c26ae3e1ba7', "$eid: The binary attachment's md5sum matches"); + } + else + { + like($acontent, qr/zanzibar/, "$eid: The attachment isn't screwed up in the database."); + } + } + + return 0; +} + diff --git a/rt/t/mail/smime/reject_on_unencrypted.t b/rt/t/mail/smime/reject_on_unencrypted.t new file mode 100644 index 000000000..ab62d83fc --- /dev/null +++ b/rt/t/mail/smime/reject_on_unencrypted.t @@ -0,0 +1,137 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef, actual_server => 1, config => 'Set( %Crypt, RejectOnUnencrypted => 1 );'; +my $test = 'RT::Test::SMIME'; + +use IPC::Run3 'run3'; +use String::ShellQuote 'shell_quote'; +use RT::Tickets; + +my ($url, $m) = RT::Test->started_ok; +ok $m->login, "logged in"; + +# configure key for General queue +RT::Test::SMIME->import_key('sender@example.com'); +my $queue = RT::Test->load_or_create_queue( + Name => 'General', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +my $user = RT::Test->load_or_create_user( + Name => 'root@example.com', + EmailAddress => 'root@example.com', +); +RT::Test::SMIME->import_key('root@example.com.crt', $user); +RT::Test->add_rights( Principal => $user, Right => 'SuperUser', Object => RT->System ); + +my $mail = RT::Test->open_mailgate_ok($url); +print $mail <<EOF; +From: root\@localhost +To: rt\@$RT::rtname +Subject: This is a test of new ticket creation as root + +Blah! +Foob! +EOF +RT::Test->close_mailgate_ok($mail); + +{ + ok(!RT::Test->last_ticket, 'A ticket was not created'); + my ($mail) = RT::Test->fetch_caught_mails; + like( + $mail, + qr/^Subject: RT requires that all incoming mail be encrypted/m, + 'rejected mail that is not encrypted' + ); + my ($warning) = $m->get_warnings; + like($warning, qr/rejected because the message is unencrypted/); +} + +{ + # test for encrypted mail + my $buf = ''; + run3( + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted message for queue", + $test->key_path('sender@example.com.crt' ), + ), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + is ($status >> 8, 0, "The mail gateway exited normally"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + is( $tick->Subject, 'Encrypted message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + is( $msg->GetHeader('X-RT-Privacy'), + 'SMIME', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orz'); + + is( $orig->GetHeader('Content-Type'), 'application/x-rt-original-message'); +} + +{ + my $buf = ''; + + run3( + join( + ' ', + shell_quote( + RT->Config->Get('SMIME')->{'OpenSSL'}, + qw( smime -sign -nodetach -passin pass:123456), + -signer => $test->key_path('root@example.com.crt' ), + -inkey => $test->key_path('root@example.com.key' ), + ), + '|', + shell_quote( + qw(openssl smime -encrypt -des3), + -from => 'root@example.com', + -to => 'sender@example.com', + -subject => "Encrypted and signed message for queue", + $test->key_path('sender@example.com.crt' ), + )), + \"Subject: test\n\norzzzzzz", + \$buf, + \*STDERR + ); + + my ($status, $tid) = RT::Test->send_via_mailgate( $buf ); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $tid ); + ok( $tick->Id, "found ticket " . $tick->Id ); + is( $tick->Subject, 'Encrypted and signed message for queue', + "Created the ticket" + ); + + my $txn = $tick->Transactions->First; + my ($msg, $attach, $orig) = @{$txn->Attachments->ItemsArrayRef}; + is( $msg->GetHeader('X-RT-Incoming-Encryption'), + 'Success', + 'recorded incoming mail that is encrypted' + ); + like( $attach->Content, qr'orzzzz'); +} + +undef $m; +done_testing; diff --git a/rt/t/mail/specials-in-encodedwords.t b/rt/t/mail/specials-in-encodedwords.t index f9da9c6e9..36efcd5e7 100644 --- a/rt/t/mail/specials-in-encodedwords.t +++ b/rt/t/mail/specials-in-encodedwords.t @@ -14,7 +14,7 @@ diag "specials (, and ;) in MIME encoded-words aren't treated as specials"; From: root@localhost Subject: testing mime encoded specials Cc: a@example.com, =?utf8?q?d=40example.com=2ce=40example.com=3b?= - <b@example.com>; c@example.com + <b@example.com>, c@example.com Content-Type: text/plain; charset=utf8 here's some content diff --git a/rt/t/mail/wrong_mime_charset.t b/rt/t/mail/wrong_mime_charset.t index 6bbaca1bb..a3986d72f 100644 --- a/rt/t/mail/wrong_mime_charset.t +++ b/rt/t/mail/wrong_mime_charset.t @@ -1,6 +1,6 @@ use strict; use warnings; -use RT::Test nodb => 1, tests => 6; +use RT::Test nodb => 1, tests => undef; use_ok('RT::I18N'); my $test_string = Encode::decode("UTF-8", 'À'); @@ -20,10 +20,6 @@ local $SIG{__WARN__} = sub { RT::I18N::SetMIMEEntityToEncoding( $mime, 'iso-8859-1' ); -TODO: { - local $TODO = -'need a better approach of encoding converter, should be fixed in 4.2'; - # this is a weird behavior for different perl versions, 5.12 warns twice, # which is correct since we do the encoding thing twice, for Subject # and Data respectively. @@ -44,4 +40,5 @@ is( $subject, $test_string, 'subject is set to iso-8859-1' ); my $body = Encode::decode( 'iso-8859-1', $mime->stringify_body ); chomp $body; is( $body, $test_string, 'body is set to iso-8859-1' ); -} + +done_testing; |