Merge branch 'patch-5' of https://github.com/gjones2/Freeside (#13854 as this bug...
[freeside.git] / rt / t / mail / sendmail.t
1 #!/usr/bin/perl -w
2
3 use strict;
4 use File::Spec ();
5
6 use RT::Test tests => 141;
7
8 use RT::EmailParser;
9 use RT::Tickets;
10 use RT::Action::SendEmail;
11
12 my @_outgoing_messages;
13 my @scrips_fired;
14
15 #We're not testing acls here.
16 my $everyone = RT::Group->new(RT->SystemUser);
17 $everyone->LoadSystemInternalGroup('Everyone');
18 $everyone->PrincipalObj->GrantRight( Right =>'SuperUser' );
19
20
21 is (__PACKAGE__, 'main', "We're operating in the main package");
22
23 {
24     no warnings qw/redefine/;
25     *RT::Action::SendEmail::SendMessage = sub {
26         my $self = shift;
27         my $MIME = shift;
28
29         main::_fired_scrip($self->ScripObj);
30         main::is(ref($MIME) , 'MIME::Entity', "hey, look. it's a mime entity");
31     };
32 }
33
34 # some utils
35 sub first_txn    { return $_[0]->Transactions->First }
36 sub first_attach { return first_txn($_[0])->Attachments->First }
37
38 sub count_txns { return $_[0]->Transactions->Count }
39 sub count_attachs { return first_txn($_[0])->Attachments->Count }
40
41 # instrument SendEmail to pass us what it's about to send.
42 # create a regular ticket
43
44 my $parser = RT::EmailParser->new();
45
46 # Let's test to make sure a multipart/report is processed correctly
47 my $multipart_report_email = RT::Test::get_relocatable_file('multipart-report',
48     (File::Spec->updir(), 'data', 'emails'));
49 my $content =  RT::Test->file_content($multipart_report_email);
50 # be as much like the mail gateway as possible.
51 use RT::Interface::Email;
52 my %args =        (message => $content, queue => 1, action => 'correspond');
53 my ($status, $msg) = RT::Interface::Email::Gateway(\%args);
54 ok($status, "successfuly used Email::Gateway interface") or diag("error: $msg");
55 my $tickets = RT::Tickets->new(RT->SystemUser);
56 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
57 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
58 my $tick= $tickets->First();
59 isa_ok($tick, "RT::Ticket", "got a ticket object");
60 ok ($tick->Id, "found ticket ".$tick->Id);
61 like (first_txn($tick)->Content , qr/The original message was received/, "It's the bounce");
62
63
64 # make sure it fires scrips.
65 is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
66
67 undef @scrips_fired;
68
69
70
71
72 $parser->ParseMIMEEntityFromScalar('From: root@localhost
73 To: rt@example.com
74 Subject: This is a test of new ticket creation as an unknown user
75
76 Blah!
77 Foob!');
78
79                                   
80 use Data::Dumper;
81
82 my $ticket = RT::Ticket->new(RT->SystemUser);
83 my  ($id,  undef, $create_msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity);
84 ok ($id,$create_msg);
85 $tickets = RT::Tickets->new(RT->SystemUser);
86 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
87 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
88  $tick = $tickets->First();
89 ok ($tick->Id, "found ticket ".$tick->Id);
90 is ($tick->Subject , 'I18NTest', "failed to create the new ticket from an unprivileged account");
91
92 # make sure it fires scrips.
93 is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
94 # make sure it sends an autoreply
95 # make sure it sends a notification to adminccs
96
97
98 # we need to swap out SendMessage to test the new things we care about;
99 &utf8_redef_sendmessage;
100
101 # create an iso 8859-1 ticket
102 @scrips_fired = ();
103
104 my $iso_8859_1_ticket_email = RT::Test::get_relocatable_file(
105     'new-ticket-from-iso-8859-1', (File::Spec->updir(), 'data', 'emails'));
106 $content =  RT::Test->file_content($iso_8859_1_ticket_email);
107
108
109
110 $parser->ParseMIMEEntityFromScalar($content);
111
112
113 # be as much like the mail gateway as possible.
114 use RT::Interface::Email;
115                            
116  %args =        (message => $content, queue => 1, action => 'correspond');
117  RT::Interface::Email::Gateway(\%args);
118  $tickets = RT::Tickets->new(RT->SystemUser);
119 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
120 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
121  $tick = $tickets->First();
122 ok ($tick->Id, "found ticket ".$tick->Id);
123
124 like (first_txn($tick)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay");
125
126
127 # make sure it fires scrips.
128 is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
129 # make sure it sends an autoreply
130
131
132 # make sure it sends a notification to adminccs
133
134 # If we correspond, does it do the right thing to the outbound messages?
135
136 $parser->ParseMIMEEntityFromScalar($content);
137   ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
138 ok ($id, $msg);
139
140 $parser->ParseMIMEEntityFromScalar($content);
141 ($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
142 ok ($id, $msg);
143
144
145
146
147
148 # we need to swap out SendMessage to test the new things we care about;
149 &iso8859_redef_sendmessage;
150 RT->Config->Set( EmailOutputEncoding => 'iso-8859-1' );
151 # create an iso 8859-1 ticket
152 @scrips_fired = ();
153
154  $content =  RT::Test->file_content($iso_8859_1_ticket_email);
155 # be as much like the mail gateway as possible.
156 use RT::Interface::Email;
157                                   
158  %args =        (message => $content, queue => 1, action => 'correspond');
159  RT::Interface::Email::Gateway(\%args);
160 $tickets = RT::Tickets->new(RT->SystemUser);
161 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
162 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
163  $tick = $tickets->First();
164 ok ($tick->Id, "found ticket ".$tick->Id);
165
166 like (first_txn($tick)->Content , qr/H\x{e5}vard/, "It's signed by havard. yay");
167
168
169 # make sure it fires scrips.
170 is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
171 # make sure it sends an autoreply
172
173
174 # make sure it sends a notification to adminccs
175
176
177 # If we correspond, does it do the right thing to the outbound messages?
178
179 $parser->ParseMIMEEntityFromScalar($content);
180  ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
181 ok ($id, $msg);
182
183 $parser->ParseMIMEEntityFromScalar($content);
184 ($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
185 ok ($id, $msg);
186
187
188 sub _fired_scrip {
189         my $scrip = shift;
190         push @scrips_fired, $scrip;
191 }       
192
193 sub utf8_redef_sendmessage {
194     no warnings qw/redefine/;
195     *RT::Action::SendEmail::SendMessage = sub {
196         my $self = shift;
197         my $MIME = shift;
198
199         my $scrip = $self->ScripObj->id;
200         ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
201         main::_fired_scrip($self->ScripObj);
202         $MIME->make_singlepart;
203         main::is( ref($MIME) , 'MIME::Entity',
204                   "hey, look. it's a mime entity" );
205         main::is( ref( $MIME->head ) , 'MIME::Head',
206                   "its mime header is a mime header. yay" );
207         main::like( $MIME->head->get('Content-Type') , qr/utf-8/,
208                   "Its content type is utf-8" );
209         my $message_as_string = $MIME->bodyhandle->as_string();
210         use Encode;
211         $message_as_string = Encode::decode_utf8($message_as_string);
212         main::like(
213             $message_as_string , qr/H\x{e5}vard/,
214 "The message's content contains havard's name. this will fail if it's not utf8 out");
215
216     };
217 }
218
219 sub iso8859_redef_sendmessage {
220     no warnings qw/redefine/;
221     *RT::Action::SendEmail::SendMessage = sub {
222         my $self = shift;
223         my $MIME = shift;
224
225         my $scrip = $self->ScripObj->id;
226         ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
227         main::_fired_scrip($self->ScripObj);
228         $MIME->make_singlepart;
229         main::is( ref($MIME) , 'MIME::Entity',
230                   "hey, look. it's a mime entity" );
231         main::is( ref( $MIME->head ) , 'MIME::Head',
232                   "its mime header is a mime header. yay" );
233         main::like( $MIME->head->get('Content-Type') , qr/iso-8859-1/,
234                   "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") );
235         my $message_as_string = $MIME->bodyhandle->as_string();
236         use Encode;
237         $message_as_string = Encode::decode("iso-8859-1",$message_as_string);
238         main::like(
239             $message_as_string , qr/H\x{e5}vard/, "The message's content contains havard's name. this will fail if it's not utf8 out");
240     };
241 }
242
243
244  my $alt_umlaut_email = RT::Test::get_relocatable_file(
245      'multipart-alternative-with-umlaut', (File::Spec->updir(), 'data', 'emails'));
246  $content =  RT::Test->file_content($alt_umlaut_email);
247
248 $parser->ParseMIMEEntityFromScalar($content);
249
250
251 # be as much like the mail gateway as possible.
252 {
253     no warnings qw/redefine/;
254     local *RT::Action::SendEmail::SendMessage = sub { return 1};
255
256     %args = (message => $content, queue => 1, action => 'correspond');
257     RT::Interface::Email::Gateway(\%args);
258     # TODO: following 5 lines should replaced by get_latest_ticket_ok()
259     $tickets = RT::Tickets->new(RT->SystemUser);
260     $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
261     $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
262     $tick = $tickets->First();
263
264     ok ($tick->Id, "found ticket ".$tick->Id);
265
266     like (first_txn($tick)->Content , qr/causes Error/, "We recorded the content right as text-plain");
267     is (count_attachs($tick) , 3 , "Has three attachments, presumably a text-plain, a text-html and a multipart alternative");
268
269 }
270
271
272  my $text_html_email = RT::Test::get_relocatable_file('text-html-with-umlaut',
273      (File::Spec->updir(), 'data', 'emails'));
274  $content =  RT::Test->file_content($text_html_email);
275
276 $parser->ParseMIMEEntityFromScalar($content);
277
278
279 # be as much like the mail gateway as possible.
280 &text_html_redef_sendmessage;
281
282  %args =        (message => $content, queue => 1, action => 'correspond');
283  RT::Interface::Email::Gateway(\%args);
284  $tickets = RT::Tickets->new(RT->SystemUser);
285 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
286 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
287  $tick = $tickets->First();
288 ok ($tick->Id, "found ticket ".$tick->Id);
289
290 like (first_attach($tick)->Content , qr/causes Error/, "We recorded the content as containing 'causes error'") or diag( first_attach($tick)->Content );
291 like (first_attach($tick)->ContentType , qr/text\/html/, "We recorded the content as text/html");
292 is (count_attachs($tick), 1 , "Has one attachment, presumably a text-html and a multipart alternative");
293
294 sub text_html_redef_sendmessage {
295     no warnings qw/redefine/;
296     *RT::Action::SendEmail::SendMessage = sub {
297         my $self = shift;
298         my $MIME = shift;
299         return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
300         is ($MIME->parts, 0, "generated correspondence mime entity
301                 does not have parts");
302         is ($MIME->head->mime_type , "text/plain", "The mime type is a plain");
303     };
304 }
305
306
307  my $russian_email = RT::Test::get_relocatable_file('text-html-in-russian',
308      (File::Spec->updir(), 'data', 'emails'));
309  $content =  RT::Test->file_content($russian_email);
310
311 $parser->ParseMIMEEntityFromScalar($content);
312
313 # be as much like the mail gateway as possible.
314 &text_html_redef_sendmessage;
315
316  %args =        (message => $content, queue => 1, action => 'correspond');
317
318 {
319
320 my @warnings;
321 local $SIG{__WARN__} = sub {
322     push @warnings, "@_";
323 };
324
325 RT::Interface::Email::Gateway(\%args);
326
327 TODO: {
328         local $TODO =
329 'need a better approach of encoding converter, should be fixed in 4.2';
330 ok( @warnings == 1 || @warnings == 2, "1 or 2 warnings are ok" );
331 ok( @warnings == 1 || ( @warnings == 2 && $warnings[1] eq $warnings[0] ),
332     'if there are 2 warnings, they should be same' );
333
334 like(
335     $warnings[0],
336     qr/\QEncoding error: "\x{041f}" does not map to iso-8859-1/,
337 "The badly formed Russian spam we have isn't actually well-formed UTF8, which makes Encode (correctly) warn",
338 );
339
340 }
341 }
342
343  $tickets = RT::Tickets->new(RT->SystemUser);
344 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
345 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
346  $tick = $tickets->First();
347 ok ($tick->Id, "found ticket ".$tick->Id);
348
349 like (first_attach($tick)->ContentType , qr/text\/html/, "We recorded the content right as text-html");
350
351 is (count_attachs($tick) ,1 , "Has one attachment, presumably a text-html and a multipart alternative");
352
353
354
355 RT->Config->Set( EmailInputEncodings => 'koi8-r', RT->Config->Get('EmailInputEncodings') );
356 RT->Config->Set( EmailOutputEncoding => 'koi8-r' );
357 my $russian_subject_email = RT::Test::get_relocatable_file(
358     'russian-subject-no-content-type', (File::Spec->updir(), 'data', 'emails'));
359 $content = RT::Test->file_content($russian_subject_email);
360
361 $parser->ParseMIMEEntityFromScalar($content);
362
363
364 # be as much like the mail gateway as possible.
365 &text_plain_russian_redef_sendmessage;
366  %args =        (message => $content, queue => 1, action => 'correspond');
367  RT::Interface::Email::Gateway(\%args);
368  $tickets = RT::Tickets->new(RT->SystemUser);
369 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
370 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
371 $tick= $tickets->First();
372 ok ($tick->Id, "found ticket ".$tick->Id);
373
374 like (first_attach($tick)->ContentType , qr/text\/plain/, "We recorded the content type right");
375 is (count_attachs($tick) ,1 , "Has one attachment, presumably a text-plain");
376 is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right");
377 sub text_plain_russian_redef_sendmessage {
378     no warnings qw/redefine/;
379     *RT::Action::SendEmail::SendMessage = sub {
380         my $self = shift; 
381         my $MIME = shift; 
382         return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
383         is ($MIME->head->mime_type , "text/plain", "The only part is text/plain ");
384             my $subject  = $MIME->head->get("subject");
385         chomp($subject);
386         #is( $subject ,      /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly");
387     };
388 }
389
390 my @input_encodings = RT->Config->Get( 'EmailInputEncodings' );
391 shift @input_encodings;
392 RT->Config->Set(EmailInputEncodings => @input_encodings );
393 RT->Config->Set(EmailOutputEncoding => 'utf-8');
394
395
396
397 my $nested_rfc822_email = RT::Test::get_relocatable_file('nested-rfc-822',
398     (File::Spec->updir(), 'data', 'emails'));
399 $content =  RT::Test->file_content($nested_rfc822_email);
400 ok ($content, "Loaded nested-rfc-822 to test");
401
402 $parser->ParseMIMEEntityFromScalar($content);
403
404
405 # be as much like the mail gateway as possible.
406 &text_plain_nested_redef_sendmessage;
407  %args =        (message => $content, queue => 1, action => 'correspond');
408  RT::Interface::Email::Gateway(\%args);
409  $tickets = RT::Tickets->new(RT->SystemUser);
410 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
411 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
412 $tick= $tickets->First();
413 ok ($tick->Id, "found ticket ".$tick->Id);
414 is ($tick->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?");
415 like (first_attach($tick)->ContentType , qr/multipart\/mixed/, "We recorded the content type right");
416 is (count_attachs($tick) , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain");
417 sub text_plain_nested_redef_sendmessage {
418     no warnings qw/redefine/;
419     *RT::Action::SendEmail::SendMessage = sub {
420         my $self = shift;
421         my $MIME = shift;
422
423         return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
424
425         is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart");
426
427         use MIME::Words qw(:all);
428         my $encoded_subject = $MIME->head->get("subject");
429         my $subject = decode_mimewords($encoded_subject);
430
431         # MIME::Words isn't actually UTF8-safe. There go 4 hours I'll never get back.
432         utf8::decode($subject);
433         like($subject, qr/Niv\x{e5}er/, "The subject matches the word - $subject");
434
435         1;
436     };
437 }
438
439
440
441
442  my $uuencoded_email = RT::Test::get_relocatable_file('notes-uuencoded',
443      (File::Spec->updir(), 'data', 'emails'));
444  $content =  RT::Test->file_content($uuencoded_email);
445
446 $parser->ParseMIMEEntityFromScalar($content);
447
448
449 # be as much like the mail gateway as possible.
450 {
451     no warnings qw/redefine/;
452     local *RT::Action::SendEmail::SendMessage = sub { return 1};
453     %args =        (message => $content, queue => 1, action => 'correspond');
454     RT::Interface::Email::Gateway(\%args);
455     $tickets = RT::Tickets->new(RT->SystemUser);
456     $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
457     $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
458     $tick= $tickets->First();
459     ok ($tick->Id, "found ticket ".$tick->Id);
460
461     like (first_txn($tick)->Content , qr/from Lotus Notes/, "We recorded the content right");
462     is (count_attachs($tick) , 3 , "Has three attachments");
463 }
464
465
466
467  my $crashes_file_based_parser_email = RT::Test::get_relocatable_file(
468      'crashes-file-based-parser', (File::Spec->updir(), 'data', 'emails'));
469  $content = RT::Test->file_content($crashes_file_based_parser_email);
470
471 $parser->ParseMIMEEntityFromScalar($content);
472
473
474 # be as much like the mail gateway as possible.
475
476 no warnings qw/redefine/;
477 local *RT::Action::SendEmail::SendMessage = sub { return 1};
478  %args =        (message => $content, queue => 1, action => 'correspond');
479  RT::Interface::Email::Gateway(\%args);
480  $tickets = RT::Tickets->new(RT->SystemUser);
481 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
482 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
483 $tick= $tickets->First();
484 ok ($tick->Id, "found ticket ".$tick->Id);
485
486 like (first_txn($tick)->Content , qr/FYI/, "We recorded the content right");
487 is (count_attachs($tick) , 5 , "Has three attachments");
488
489
490
491
492
493
494  my $rt_send_cc_email = RT::Test::get_relocatable_file('rt-send-cc',
495      (File::Spec->updir(), 'data', 'emails'));
496  $content =  RT::Test->file_content($rt_send_cc_email);
497
498 $parser->ParseMIMEEntityFromScalar($content);
499
500
501
502  %args =        (message => $content, queue => 1, action => 'correspond');
503  RT::Interface::Email::Gateway(\%args);
504  $tickets = RT::Tickets->new(RT->SystemUser);
505 $tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
506 $tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
507 $tick= $tickets->First();
508 ok ($tick->Id, "found ticket ".$tick->Id);
509
510 my $cc = first_attach($tick)->GetHeader('RT-Send-Cc');
511 like ($cc , qr/test1/, "Found test 1");
512 like ($cc , qr/test2/, "Found test 2");
513 like ($cc , qr/test3/, "Found test 3");
514 like ($cc , qr/test4/, "Found test 4");
515 like ($cc , qr/test5/, "Found test 5");
516
517
518 diag q{regression test for #5248 from rt3.fsck.com};
519 {
520     my $subject_folding_email = RT::Test::get_relocatable_file(
521         'subject-with-folding-ws', (File::Spec->updir(), 'data', 'emails'));
522     my $content = RT::Test->file_content($subject_folding_email);
523     my ($status, $msg, $ticket) = RT::Interface::Email::Gateway(
524         { message => $content, queue => 1, action => 'correspond' }
525     );
526     ok ($status, 'created ticket') or diag "error: $msg";
527     ok ($ticket->id, "found ticket ". $ticket->id);
528     is ($ticket->Subject, 'test', 'correct subject');
529 }
530
531 diag q{regression test for #5248 from rt3.fsck.com};
532 {
533     my $long_subject_email = RT::Test::get_relocatable_file('very-long-subject',
534         (File::Spec->updir(), 'data', 'emails'));
535     my $content = RT::Test->file_content($long_subject_email);
536     my ($status, $msg, $ticket) = RT::Interface::Email::Gateway(
537         { message => $content, queue => 1, action => 'correspond' }
538     );
539     ok ($status, 'created ticket') or diag "error: $msg";
540     ok ($ticket->id, "found ticket ". $ticket->id);
541     is ($ticket->Subject, '0123456789'x20, 'correct subject');
542 }
543
544
545
546 # Don't taint the environment
547 $everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');