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