Merge branch '2013.03' into 2013.10
[tine20] / tests / tine20 / Felamimail / Controller / MessageTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  *
5  * @package     Felamimail
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  *
10  */
11
12 /**
13  * Test helper
14  */
15 require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
16
17 /**
18  * Test class for Tinebase_Group
19  */
20 class Felamimail_Controller_MessageTest extends PHPUnit_Framework_TestCase
21 {
22     /**
23      * @var Felamimail_Controller_Message
24      */
25     protected $_controller = NULL;
26     
27     /**
28      * @var Felamimail_Model_Account
29      */
30     protected $_account = NULL;
31     
32     /**
33      * keep track of created messages
34      *
35      * @var Tinebase_Record_RecordSet
36      */
37     protected $_createdMessages;
38     
39     /**
40      * @var Felamimail_Backend_Imap
41      */
42     protected $_imap = NULL;
43     
44     /**
45      * @var Felamimail_Controller_Cache_Message
46      */
47     protected $_cache;
48     
49     /**
50      * @var Felamimail_Model_Folder
51      */
52     protected $_folder = NULL;
53     
54     /**
55      * name of the folder to use for tests
56      * @var string
57      */
58     protected $_testFolderName = 'Junk';
59     
60     /**
61      * accounts to delete in tearDown
62      *
63      * @var array
64      */
65     protected $_accountsToDelete = array();
66     
67     /**
68      * Runs the test methods of this class.
69      *
70      * @access public
71      * @static
72      */
73     public static function main()
74     {
75         $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Felamimail Message Controller Tests');
76         PHPUnit_TextUI_TestRunner::run($suite);
77     }
78
79     /**
80      * Sets up the fixture.
81      * This method is called before a test is executed.
82      *
83      * @access protected
84      */
85     protected function setUp()
86     {
87         $this->_account    = Felamimail_Controller_Account::getInstance()->search()->getFirstRecord();
88         $this->_controller = Felamimail_Controller_Message::getInstance();
89         $this->_imap       = Felamimail_Backend_ImapFactory::factory($this->_account);
90         
91         $this->_folder     = $this->getFolder($this->_testFolderName);
92         $this->_imap->selectFolder($this->_testFolderName);
93         $this->_cache      = Felamimail_Controller_Cache_Message::getInstance();
94         $this->_createdMessages = new Tinebase_Record_RecordSet('Felamimail_Model_Message');
95     }
96
97     /**
98      * Tears down the fixture
99      * This method is called after a test is executed.
100      *
101      * @access protected
102      */
103     protected function tearDown()
104     {
105         try {
106             Felamimail_Controller_Message_Flags::getInstance()->addFlags($this->_createdMessages, array(Zend_Mail_Storage::FLAG_DELETED));
107         } catch (Zend_Mail_Storage_Exception $zmse) {
108             // do nothing
109         }
110         
111         foreach ($this->_accountsToDelete as $account) {
112             Felamimail_Controller_Account::getInstance()->delete($account);
113         }
114     }
115
116     /********************************* test funcs *************************************/
117     
118     /**
119      * test getting multiple messages
120      * 
121      * @group longrunning
122      */
123     public function testGetMultipleMessages()
124     {
125         $message1 = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
126         $message2 = $this->messageTestHelper('text_plain.eml', 'text/plain');
127         
128         $messages = $this->_controller->getMultiple(array(
129             $message1->getId(),
130             $message2->getId()
131         ));
132         
133         $this->assertEquals(2, count($messages));
134     }
135     
136     /**
137      * test search with cache
138      * - test text_plain.eml message
139      * - test from header
140      */
141     public function testSearchWithCache()
142     {
143         // get inbox folder id
144         Felamimail_Controller_Cache_Folder::getInstance()->update($this->_account->getId());
145         $folderBackend = new Felamimail_Backend_Folder();
146         $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $this->_testFolderName);
147         
148         // clear cache and empty folder
149         $this->_cache->clear($folder->getId());
150         Felamimail_Controller_Folder::getInstance()->emptyFolder($folder->getId());
151         
152         // append message
153         $this->_appendMessage('text_plain.eml', $this->_folder);
154         
155         // search messages in test folder
156         $this->_cache->updateCache($folder);
157         $result = $this->_controller->search($this->_getFilter($folder->getId()));
158         
159         //print_r($result->toArray());
160         
161         // check result
162         $firstMessage = $result->getFirstRecord();
163         $this->_createdMessages->addRecord($firstMessage);
164
165         $this->assertGreaterThan(0, count($result));
166         $this->assertEquals($folder->getId(), $firstMessage->folder_id);
167         $this->assertEquals("Re: [gentoo-dev] `paludis --info' is not like `emerge --info'", $firstMessage->subject);
168         $this->assertEquals('Pipping, Sebastian (Luxembourg)', $firstMessage->from_name);
169         $this->assertEquals('webmaster@changchung.org', $firstMessage->from_email);
170         $this->assertEquals(array('gentoo-dev@lists.gentoo.org', 'webmaster@changchung.org') , $firstMessage->to);
171         
172         // check cache entries
173         $cacheBackend = new Felamimail_Backend_Cache_Sql_Message();
174         $cachedMessage = $cacheBackend->get($firstMessage->getId());
175         $this->assertEquals($folder->getId(), $cachedMessage->folder_id);
176         $this->assertEquals(Tinebase_DateTime::now()->format('Y-m-d'), $cachedMessage->timestamp->format('Y-m-d'));
177         
178         // clear cache
179         $this->_cache->clear($folder->getId());
180     }
181     
182     /**
183      * testBodyStructureTextPlain
184      */
185     public function testBodyStructureTextPlain()
186     {
187         $expectedStructure = array(
188             'partId'      => 1,
189             'contentType' => 'text/plain',
190             'type'        => 'text',
191             'subType'     => 'plain',
192             'parameters'  => array (
193                 'charset' => 'ISO-8859-1'
194             ),
195             'id'          => '',
196             'description' => '',
197             'encoding'    => '7bit',
198             'size'        => 388,
199             'disposition' => '',
200             'language'    => '',
201             'location'    => '',
202             
203         );
204
205         $message = $this->messageTestHelper('text_plain.eml', 'text/plain');
206         
207         $lines = $message['structure']['lines'];
208         $structure = $message['structure'];
209         unset($structure['lines']);
210         
211         $this->assertEquals($expectedStructure, $structure, 'structure does not match');
212         // dbmail always has one more line than dovecot
213         $this->assertTrue(in_array($lines, array(17, 18)));
214     }
215     
216     /**
217      * testBodyStructureMultipartAlternative
218      */
219     public function testBodyStructureMultipartAlternative()
220     {
221         $expectedStructure = array(
222             'partId'      => null,
223             'contentType' => 'multipart/alternative',
224             'type'        => 'multipart',
225             'subType'     => 'alternative',
226             'parts'       => array(
227                 1 => array(
228                     'partId'      => 1,
229                     'contentType' => 'text/plain',
230                     'type'        => 'text',
231                     'subType'     => 'plain',
232                     'parameters'  => array (
233                         'charset' => 'iso-8859-1'
234                     ),
235                     'id'          => '',
236                     'description' => '',
237                     'encoding'    => 'quoted-printable',
238                     'size'        => 1726,
239                     'disposition' => '',
240                     'language'    => '',
241                     'location'    => '',
242                 ),
243                 2 => array(
244                     'partId'      => 2,
245                     'contentType' => 'text/html',
246                     'type'        => 'text',
247                     'subType'     => 'html',
248                     'parameters'  => array (
249                         'charset' => 'iso-8859-1'
250                     ),
251                     'id'          => '',
252                     'description' => '',
253                     'encoding'    => 'quoted-printable',
254                     'size'        => 10713,
255                     'disposition' => '',
256                     'language'    => '',
257                     'location'    => '',
258                 )
259             ),
260             'parameters'  => array (
261                 'boundary' => '=_m192h4woyec67braywzx'
262             ),
263             'disposition' => '',
264             'language'    => '',
265             'location'    => '',
266             
267         );
268         
269         $message = $this->messageTestHelper('multipart_alternative.eml', 'multipart/alternative');
270         $structure = $message['structure'];
271         $lines = $this->_getLinesFromPartsAndRemoveFromStructure($structure);
272         
273         $this->assertEquals($expectedStructure, $structure, 'structure does not match');
274         $this->assertTrue(in_array($lines[1], array(49, 50)));
275         $this->assertTrue(in_array($lines[2], array(172, 173)));
276     }
277     
278     /**
279      * get lines from structure parts and remove them from structure array
280      *
281      * @param array $_structure
282      * @return array
283      */
284     protected function _getLinesFromPartsAndRemoveFromStructure(&$_structure)
285     {
286         $lines = array();
287         foreach ($_structure['parts'] as $key => $part) {
288             $lines[$key] = $part['lines'];
289             unset($_structure['parts'][$key]['lines']);
290         }
291         
292         return $lines;
293     }
294     
295     /**
296      * testBodyStructureMultipartMixed
297      */
298     public function testBodyStructureMultipartMixed()
299     {
300         $expectedStructure = array(
301             'partId'      => null,
302             'contentType' => 'multipart/mixed',
303             'type'        => 'multipart',
304             'subType'     => 'mixed',
305             'parts'       => array(
306                 1 => array(
307                     'partId'      => 1,
308                     'contentType' => Felamimail_Model_Message::CONTENT_TYPE_PLAIN,
309                     'type'        => 'text',
310                     'subType'     => 'plain',
311                     'parameters'  => array (
312                         'charset' => 'us-ascii'
313                     ),
314                     'id'          => null,
315                     'description' => null,
316                     'encoding'    => '7bit',
317                     'size'        => 3896,
318                     'disposition' => array(
319                         'type'    => 'inline'
320                     ),
321                     'language'    => '',
322                     'location'    => '',
323                 ),
324                 2 => array(
325                     'partId'      => 2,
326                     'contentType' => Felamimail_Model_Message::CONTENT_TYPE_PLAIN,
327                     'type'        => 'text',
328                     'subType'     => 'plain',
329                     'parameters'  => array (
330                         'charset' => 'us-ascii'
331                     ),
332                     'id'          => '',
333                     'description' => '',
334                     'encoding'    => '7bit',
335                     'size'        => 2787,
336                     'disposition' => array(
337                         'type'    => 'attachment',
338                     ),
339                     'language'    => '',
340                     'location'    => '',
341                 )
342             ),
343             'parameters'  => array (
344                 'boundary' => '0F1p//8PRICkK4MWrobbat28989323553773'
345             ),
346             'disposition' => array(
347                 'type'    => 'inline'
348             ),
349             'language'    => '',
350             'location'    => '',
351         );
352         
353         $expectedParameters = array(
354             'foobar'   => 'Test Subjäct',
355             'filename' => 'add-removals.1239580800.log'
356         );
357         
358         $message = $this->messageTestHelper('multipart_mixed.eml', 'multipart/mixed');
359         $structure = $message['structure'];
360         $lines = $this->_getLinesFromPartsAndRemoveFromStructure($structure);
361         // attachment parameters could have different order
362         $parameters = $structure['parts'][2]['disposition']['parameters'];
363         unset($structure['parts'][2]['disposition']['parameters']);
364         
365         $this->assertEquals($expectedStructure, $structure, 'structure does not match');
366         $this->assertEquals(Felamimail_Model_Message::CONTENT_TYPE_PLAIN, $message['body_content_type']);
367         $this->assertTrue(in_array($lines[1], array(61, 62)));
368         $this->assertTrue(in_array($lines[2], array(52, 53)));
369         $this->assertTrue($expectedParameters == $parameters);
370     }
371     
372     /**
373      * testBodyStructureMultipartMixedWithMessageRFC822
374      */
375     public function testBodyStructureMultipartMixedWithMessageRFC822()
376     {
377         $expectedStructure = array(
378             'partId'      => null,
379             'contentType' => 'multipart/mixed',
380             'type'        => 'multipart',
381             'subType'     => 'mixed',
382             'parts'       => array(
383                 1 => array(
384                     'partId'      => 1,
385                     'contentType' => 'text/plain',
386                     'type'        => 'text',
387                     'subType'     => 'plain',
388                     'parameters'  => array (
389                         'charset' => 'ISO-8859-1',
390                         'format'  => 'flowed'
391                     ),
392                     'id'          => null,
393                     'description' => null,
394                     'encoding'    => '7bit',
395                     'size'        => 49,
396                     'disposition' => null,
397                     'language'    => '',
398                     'location'    => '',
399                 ),
400                 2 => array(
401                     'partId'      => 2,
402                     'contentType' => 'message/rfc822',
403                     'type'        => 'message',
404                     'subType'     => 'rfc822',
405                     'parameters'  => array (
406                         'name'    => '[Officespot-cs-svn] r15209 - trunk/tine20/Tinebase.eml'
407                     ),
408                     'id'          => '',
409                     'description' => '',
410                     'encoding'    => '7bit',
411                     'size'        => 4121,
412                     'disposition' => null,
413                     'language'    => null,
414                     'location'    => null,
415                     'messageEnvelop' => array(
416                         'Wed, 30 Jun 2010 13:20:09 +0200',
417                         '[Officespot-cs-svn] r15209 - trunk/tine20/Tinebase',
418                         array(array(
419                             'NIL', 'NIL', 'c.weiss', 'metaways.de'
420                         )),
421                         array(array(
422                             'NIL', 'NIL', 'c.weiss', 'metaways.de'
423                         )),
424                         array(array(
425                             'NIL', 'NIL', 'c.weiss', 'metaways.de'
426                         )),
427                         array(array(
428                             'NIL', 'NIL', 'officespot-cs-svn', 'lists.sourceforge.net'
429                         )),
430                         'NIL',
431                         'NIL',
432                         'NIL',
433                         '<20100630112010.06CD21C059@publicsvn.hsn.metaways.net>'
434                     ),
435                     'messageStructure' => array(
436                         'partId'  => 2,
437                         'contentType' => 'text/plain',
438                         'type'        => 'text',
439                         'subType'     => 'plain',
440                         'parameters'  => array (
441                             'charset' => 'us-ascii'
442                         ),
443                         'id'          => null,
444                         'description' => null,
445                         'encoding'    => '7bit',
446                         'size'        => 1562,
447                         'disposition' => null,
448                         'language'    => '',
449                         'location'    => '',
450                     ),
451                 )
452             ),
453             'parameters'  => array (
454                 'boundary' => '------------040506070905080909080505'
455             ),
456             'disposition' => null,
457             'language'    => '',
458             'location'    => '',
459         );
460         
461         $message = $this->messageTestHelper('multipart_rfc2822.eml', 'multipart/rfc2822');
462         $structure = $message['structure'];
463         $lines = $this->_getLinesFromPartsAndRemoveFromStructure($structure);
464         $lines[3] = $structure['parts'][2]['messageStructure']['lines'];
465         $lines[4] = $structure['parts'][2]['messageLines'];
466         unset($structure['parts'][2]['messageStructure']['lines']);
467         unset($structure['parts'][2]['messageLines']);
468         // remove disposition -> dbmail finds none, dovecot does
469         $structure['parts'][2]['disposition'] = null;
470         
471         $this->assertEquals($expectedStructure, $structure, 'structure does not match');
472         $this->assertTrue(in_array($lines[1], array(4, 5)));
473         $this->assertEquals(NULL, $lines[2]);
474         $this->assertTrue(in_array($lines[3], array(33, 34)));
475         $this->assertTrue(in_array($lines[4], array(80, 81)));
476     }
477     
478     /**
479      * testGetBodyMultipartRelated
480      */
481     public function testGetBodyMultipartRelated()
482     {
483         $cachedMessage = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
484
485         $body = $this->_controller->getMessageBody($cachedMessage, null, Zend_Mime::TYPE_TEXT, $this->_account);
486         
487         $this->assertContains('würde', $body);
488     }
489     
490     /**
491      * test reading a message without setting the \Seen flag
492      */
493     public function testGetBodyMultipartRelatedReadOnly()
494     {
495         $cachedMessage = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
496
497         $body = $this->_controller->getMessageBody($cachedMessage, null, Zend_Mime::TYPE_TEXT, $this->_account, true);
498         
499         $this->assertContains('würde', $body);
500         
501         // @todo check for seen flag
502     }
503     
504     /**
505      * testGetBodyPlainText
506      */
507     public function testGetBodyPlainText()
508     {
509         $cachedMessage = $this->messageTestHelper('text_plain.eml', 'text/plain');
510         
511         $body = $this->_controller->getMessageBody($cachedMessage, null, Zend_Mime::TYPE_TEXT, $this->_account);
512         
513         $this->assertContains('a converter script be written to', $body);
514     }
515     
516     /**
517      * testGetBodyPart
518      */
519     public function testGetBodyPart()
520     {
521         $cachedMessage = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
522         
523         $part = $this->_controller->getMessagePart($cachedMessage, '2');
524         
525         $this->assertContains(Zend_Mime::MULTIPART_RELATED, $part->type);
526         $this->assertContains("------------080303000508040404000908", $part->boundary);
527         
528         $part = $this->_controller->getMessagePart($cachedMessage, '2.1');
529         
530         $this->assertContains(Zend_Mime::TYPE_HTML, $part->type);
531         $this->assertContains(Zend_Mime::ENCODING_QUOTEDPRINTABLE, $part->encoding);
532         
533         $part = $this->_controller->getMessagePart($cachedMessage, '2.2');
534         
535         $this->assertContains(Zend_Mime::DISPOSITION_ATTACHMENT, $part->disposition);
536         $this->assertContains(Zend_Mime::ENCODING_BASE64, $part->encoding);
537     }
538     
539     /**
540      * testGetCompleteMessageAsPart
541      */
542     public function testGetCompleteMessageAsPart()
543     {
544         $cachedMessage = $this->messageTestHelper('complete.eml', 'text/service');
545         
546         $messagePart = $this->_controller->getMessagePart($cachedMessage);
547         
548         ob_start();
549         fpassthru($messagePart->getRawStream());
550         $out = ob_get_clean();
551         
552         $this->assertContains('URL: https://service.metaways.net/Ticket/Display.html?id=3D59648', $out);
553     }
554         
555     /**
556      * testGetMessagePartRfc822
557      */
558     public function testGetMessagePartRfc822()
559     {
560         $cachedMessage = $this->messageTestHelper('multipart_rfc2822-2.eml', 'multipart/rfc2822-2');
561         
562         $messagePart = $this->_controller->getMessagePart($cachedMessage, 2);
563         
564         ob_start();
565         fpassthru($messagePart->getRawStream());
566         $out = ob_get_clean();
567         
568         $this->assertContains('X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12]', $out, 'header not found');
569         $this->assertContains('This component, from the feedback I have, will mostly be used on', $out, 'body not found');
570     }
571     
572     /**
573      * validate fetching a complete message
574      */
575     public function testGetCompleteMessage()
576     {
577         $cachedMessage = $this->messageTestHelper('multipart_mixed.eml', 'multipart/mixed');
578         
579         $message = $this->_controller->getCompleteMessage($cachedMessage);
580         $this->assertEquals('robbat2@gentoo.org', $message->from_email);
581         $this->assertEquals($this->_account->getId(), $message->account_id);
582         $this->assertEquals('Robin H. Johnson', $message->from_name);
583         $this->assertEquals('"Robin H. Johnson" <robbat2@stork.gentoo.org>', $message->sender);
584         $this->assertEquals('1', $message->text_partid);
585         $this->assertEquals('1', $message->has_attachment);
586         $this->assertEquals(null, $message->html_partid);
587         $this->assertEquals('9606', $message->size);
588         $this->assertContains("Automated Package Removal", $message->subject);
589         $this->assertContains('\Seen', $message->flags);
590         $this->assertContains('11AC BA4F 4778 E3F6 E4ED  F38E B27B 944E 3488 4E85', $message->body);
591         $this->assertEquals('add-removals.1239580800.log', $message->attachments[0]["filename"]);
592     }
593
594     /**
595      * validate fetching a complete message in 'other' dir and check its body
596      *
597      * howto:
598      * - copy mails to tests/tine20/Felamimail/files/other
599      * - add following header:
600      *      X-Tine20TestMessage: _filename_
601      * - run the test!
602      */
603     public function testCheckOtherMails()
604     {
605         $otherFilesDir = dirname(dirname(__FILE__)) . '/files/other';
606         if (file_exists($otherFilesDir)) {
607             foreach (new DirectoryIterator($otherFilesDir) as $item) {
608                 $filename = $item->getFileName();
609                 if ($item->isFile() && $filename !== 'README') {
610                     $fileName = 'other/' . $filename;
611                     echo "\nchecking message: " . $fileName . "\n";
612                     $cachedMessage = $this->messageTestHelper($fileName, $filename);
613                     $message = $this->_controller->getCompleteMessage($cachedMessage);
614                     echo $message->body;
615                     $this->assertTrue(! empty($message->body));
616                 }
617             }
618         }
619     }
620     
621     /**
622      * validate fetching a complete message
623      */
624     public function testGetCompleteMessage2()
625     {
626         $cachedMessage = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
627         
628         $message = $this->_controller->getCompleteMessage($cachedMessage);
629         
630         $this->assertEquals('1', $message->text_partid, 'no text part found');
631         $this->assertEquals('1', $message->has_attachment, 'no attachments found');
632         $this->assertEquals('2.1', $message->html_partid, 'no html part found');
633         $this->assertTrue(in_array($message->size, array('38455', '38506')));
634         $this->assertContains("Tine 2.0 bei Metaways", $message->subject);
635         $this->assertContains('\Seen', $message->flags);
636         $this->assertContains('Autovervollständigung', $message->body);
637         $this->assertEquals('moz-screenshot-83.png', $message->attachments[0]["filename"]);
638     }
639     
640     /**
641      * validate fetching a complete message
642      */
643     public function testGetCompleteMessage3()
644     {
645         $cachedMessage = $this->messageTestHelper('multipart_rfc2822.eml', 'multipart/rfc2822');
646         
647         $message = $this->_controller->getCompleteMessage($cachedMessage);
648         $this->assertEquals('multipart/mixed', $message->content_type);
649         $this->assertEquals('5377', $message->size);
650         $this->assertContains("Fwd: [Officespot-cs-svn] r15209 - trunk/tine20/Tinebase", $message->subject);
651         $this->assertContains('est for parsing forwarded email', $message->body);
652         $this->assertEquals('message/rfc822', $message->attachments[0]["content-type"]);
653     }
654
655     /**
656      * validate fetching a complete message from amazon
657      */
658     public function testGetCompleteMessageAmazon()
659     {
660         $cachedMessage = $this->messageTestHelper('Amazon.eml', 'multipart/amazon');
661         
662         $message = $this->_controller->getCompleteMessage($cachedMessage);
663         $this->assertEquals('multipart/alternative', $message->content_type);
664         $this->assertContains('Samsung Wave S8500 Smartphone', $message->subject);
665         $this->assertContains('Sie suchen Produkte aus der Kategorie Elektronik &amp; Foto?', $message->body);
666     }
667     
668     /**
669      * validate fetching a message from yahoo
670      *
671      * test was created for task #4680
672      */
673     public function testGetCompleteMessageYahoo()
674     {
675         $cachedMessage = $this->messageTestHelper('yahoo.eml');
676         
677         $message = $this->_controller->getCompleteMessage($cachedMessage);
678         $this->assertContains('Bitte aktualisieren Sie Ihre Kontoeinstellungen bzw. Daten-Feeds so schnell wie möglich', $message->body);
679     }
680     
681     /**
682      * validate fetching a complete message from amazon #2 -> check if images got removed correctly
683      */
684     public function testGetCompleteMessageAmazon2()
685     {
686         $cachedMessage = $this->messageTestHelper('Amazon2.eml', 'multipart/amazon2');
687         
688         $message = $this->_controller->getCompleteMessage($cachedMessage);
689         
690         $this->assertContains('Fritz Meier, wir haben Empfehlungen', $message->body);
691         $this->assertNotContains('<img', $message->body);
692         $this->assertNotContains('style="background-image:url', $message->body);
693         $this->assertNotContains('http://www.xing.com/img/xing/newsletter/navigation_bg.gif', $message->body);
694     }
695     
696     /**
697      * validate fetching a complete message from order form
698      */
699     public function testGetCompleteMessageOrder()
700     {
701         $cachedMessage = $this->messageTestHelper('Angebotsformular.eml', 'text/angebot');
702         
703         $message = $this->_controller->getCompleteMessage($cachedMessage);
704         $this->assertEquals('text/plain', $message->content_type);
705         $this->assertContains('Angebotsformular', $message->subject);
706         $this->assertContains('*Formular-Weiterleitungs-Service*', $message->body);
707     }
708
709     /**
710      * validate fetching a complete message with different encodings
711      */
712     public function testGetCompleteMessageDifferentEncoding()
713     {
714         $cachedMessage = $this->messageTestHelper('UmlauteUTF8TextISO-8859-15Signatur.eml', 'text/different');
715         
716         $message = $this->_controller->getCompleteMessage($cachedMessage);
717         //print_r($message->toArray());
718         $this->assertEquals('text/plain', $message->content_type);
719         $this->assertContains('Umlaute UTF8 Text + ISO-8859-15 Signatur', $message->subject);
720         $this->assertContains('O Ö', $message->body);
721     }
722     
723     /**
724      * validate fetching a complete message (rfc2822 part)
725      */
726     public function testGetMessageRFC822()
727     {
728         $cachedMessage = $this->messageTestHelper('multipart_rfc2822.eml', 'multipart/rfc2822');
729         
730         $message = $this->_controller->getCompleteMessage($cachedMessage, 2);
731         
732         $this->assertEquals('4121', $message->size);
733         $this->assertContains("[Officespot-cs-svn] r15209 - trunk/tine20/Tinebase", $message->subject);
734         $this->assertTrue(isset($message->body), 'no body found');
735         $this->assertContains('getLogger()-&gt;debug', $message->body);
736     }
737     
738     /**
739      * validate fetching a complete message
740      */
741     public function testGetMessageRFC822_2()
742     {
743         $cachedMessage = $this->messageTestHelper('multipart_rfc2822-2.eml', 'multipart/rfc2822-2');
744         
745         $message = $this->_controller->getCompleteMessage($cachedMessage, 2);
746         
747         $this->assertEquals('19131', $message->size);
748         $this->assertContains("Proposal: Zend_Grid", $message->subject);
749         $this->assertTrue(isset($message->body), 'no body found');
750         $this->assertContains('Bento Vilas Boas wrote', $message->body ,'string not found in body: ' . $message->body);
751         $this->assertEquals('smime.p7s', $message->attachments[0]["filename"]);
752     }
753     
754     /**
755      * validate fetching a complete message / rfc822 with base64
756      */
757     public function testGetMessageRFC822_3()
758     {
759         $cachedMessage = $this->messageTestHelper('multipart_rfc2822-3.eml', 'multipart/rfc2822-3');
760         
761         $message = $this->_controller->getCompleteMessage($cachedMessage, 2);
762         
763         $this->assertTrue(isset($message->body), 'no body found');
764         $this->assertContains('this is base64 encoded', $message->body ,'string not found in body: ' . $message->body);
765     }
766     
767     /**
768      * test adding message with duplicate to: header
769      */
770     public function testAddMessageToCacheDuplicateTo()
771     {
772         $cachedMessage = $this->messageTestHelper('text_plain2.eml', 'text_plain2.eml');
773         
774         $this->assertGreaterThan(0, count($cachedMessage->to));
775         $this->assertContains('c.weiss@metaways.de', $cachedMessage->to[0], 'wrong "to" header:' . print_r($cachedMessage->to, true));
776         $this->assertContains('online', $cachedMessage->subject);
777     }
778     
779     /**
780      * test adding message with invalid date
781      */
782     public function testAddMessageToCacheInvalidDate()
783     {
784         $cachedMessage = $this->messageTestHelper('invaliddate.eml', 'text/invaliddate');
785         
786         $this->assertEquals('1970-01-01 00:00:00', $cachedMessage->sent->toString(), print_r($cachedMessage->toArray(), true));
787     }
788     
789     /**
790      * test adding message with another invalid date
791      */
792     public function testAddMessageToCacheInvalidDate2()
793     {
794         $cachedMessage = $this->messageTestHelper('invaliddate2.eml', 'text/invaliddate2');
795         
796         $this->assertEquals('2009-03-16 19:51:23', $cachedMessage->sent->toString());
797     }
798     
799     /**
800      * test adding message with empty date header
801      */
802     public function testAddMessageToCacheEmptyDate()
803     {
804         $cachedMessage = $this->messageTestHelper('empty_date_header.eml', 'empty_date_header.eml');
805         
806         $this->assertEquals(0, $cachedMessage->sent->getTimestamp(), 'no timestamp should be set');
807     }
808     
809     /**
810      * test forward with attachment
811      * 
812      * @group longrunning
813      */
814     public function testForwardMessageWithAttachment()
815     {
816         $cachedMessage = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
817         
818         $forwardMessage = new Felamimail_Model_Message(array(
819             'account_id'    => $this->_account->getId(),
820             'subject'       => 'test forward',
821             'to'            => array($this->getEmailAddress()),
822             'body'          => 'aaaaaä <br>',
823             'headers'       => array('X-Tine20TestMessage' => Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822),
824             'original_id'   => $cachedMessage->getId(),
825             'attachments'   => array(new Tinebase_Model_TempFile(array(
826                 'type'  => Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822,
827                 'name'  => $cachedMessage->subject,
828             ), TRUE)),
829         ));
830         $sentFolder = $this->getFolder('Sent');
831
832         Felamimail_Controller_Message_Send::getInstance()->sendMessage($forwardMessage);
833         
834         $forwardedMessage = $this->searchAndCacheMessage(Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822, $this->getFolder('INBOX'));
835         $forwardedMessageInSent = $this->searchAndCacheMessage(Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822, $sentFolder);
836         $completeForwardedMessage = $this->_controller->getCompleteMessage($forwardedMessage);
837         
838         $this->assertEquals(Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822, $forwardedMessage['structure']['parts'][2]['contentType']);
839         $this->assertEquals($cachedMessage->subject . '.eml', $forwardedMessage['structure']['parts'][2]['parameters']['name'],
840             'filename mismatch in structure' . print_r($forwardedMessage['structure']['parts'][2], TRUE));
841         $this->assertEquals($cachedMessage->subject . '.eml', $completeForwardedMessage->attachments[0]['filename'],
842             'filename mismatch of attachment' . print_r($completeForwardedMessage->attachments[0], TRUE));
843         
844         return $forwardedMessage;
845     }
846     
847     /**
848      * get email address
849      *
850      * @return string
851      */
852     public function getEmailAddress()
853     {
854         $config = TestServer::getInstance()->getConfig();
855         $email = ($config->email) ? $config->email : Tinebase_Core::getUser()->accountEmailAddress;
856         
857         return $email;
858     }
859
860     /**
861      * test forward message part
862      * 
863      * @group longrunning
864      */
865     public function testForwardMessagePart()
866     {
867         $forwardedMessage = $this->testForwardMessageWithAttachment();
868         
869         $forwardMessage = new Felamimail_Model_Message(array(
870             'account_id'    => $this->_account->getId(),
871             'subject'       => 'test forward part',
872             'to'            => array($this->getEmailAddress()),
873             'body'          => 'aaaaaä <br>',
874             'headers'       => array('X-Tine20TestMessage' => Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822 . 'part'),
875             'original_id'   => $forwardedMessage->getId() . '_2', // part 2 is the original forwared message
876             'attachments'   => array(new Tinebase_Model_TempFile(array(
877                 'type'  => Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822,
878                 'name'  => $forwardedMessage->subject,
879             ), TRUE)),
880         ));
881         Felamimail_Controller_Message_Send::getInstance()->sendMessage($forwardMessage);
882         
883         $forwardedMessage = $this->searchAndCacheMessage(Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822 . 'part', $this->getFolder('INBOX'));
884         $completeForwardedMessagePart = $this->_controller->getCompleteMessage($forwardedMessage, 2);
885         
886         //print_r($completeForwardedMessagePart->toArray());
887         $this->assertTrue(! empty($completeForwardedMessagePart->headers), 'headers should not be empty');
888         $this->assertEquals('moz-screenshot-83.png', $completeForwardedMessagePart->attachments[0]['filename']);
889     }
890     
891     /**
892      * testGetBodyPartIdMultipartAlternative
893      */
894     public function testGetBodyPartIdMultipartAlternative()
895     {
896         $cachedMessage = $this->messageTestHelper('multipart_alternative.eml', 'multipart/alternative');
897         $cachedMessage->parseBodyParts();
898
899         $this->assertEquals(1, $cachedMessage->text_partid, 'did not find all partIds');
900         $this->assertEquals(2, $cachedMessage->html_partid, 'did not find all partIds');
901     }
902         
903     /**
904      * testGetBodyPartIdMultipartMixed
905      */
906     public function testGetBodyPartIdMultipartMixed()
907     {
908         $cachedMessage = $this->messageTestHelper('multipart_mixed.eml', 'multipart/mixed');
909         $cachedMessage->parseBodyParts();
910
911         $this->assertEquals(1, $cachedMessage->text_partid, 'did not find all partIds');
912     }
913     
914     /**
915      * testGetBodyPartIdMultipartSigned
916      */
917     public function testGetBodyPartIdMultipartSigned()
918     {
919         $cachedMessage = $this->messageTestHelper('multipart_signed.eml', 'multipart/signed');
920         $cachedMessage->parseBodyParts();
921
922         $this->assertEquals(1, $cachedMessage->text_partid, 'did not find all partIds');
923     }
924     
925     /**
926      * testGetBodyPartIdMultipartRelated
927      */
928     public function testGetBodyPartIdMultipartRelated()
929     {
930         $cachedMessage = $this->messageTestHelper('multipart_related.eml', 'multipart/related');
931         $cachedMessage->parseBodyParts();
932
933         $this->assertEquals(1, $cachedMessage->text_partid, 'did not find all partIds');
934         $this->assertEquals('2.1', $cachedMessage->html_partid, 'did not find all partIds');
935     }
936
937     /**
938      * testGetMessageWithoutFromHeader
939      */
940     public function testGetMessageWithoutFromHeader()
941     {
942         $cachedMessage = $this->messageTestHelper('withoutfrom.eml', 'text/withoutfrom');
943         $completeMessage = $this->_controller->getCompleteMessage($cachedMessage);
944         
945         $this->assertContains('Hier ist Ihr Hot Web Email-Deal Angebot von M&amp;M Computer.', $completeMessage->body);
946     }
947     
948     /**
949      * testGetMessageWithCommaInTo
950      */
951     public function testGetMessageWithCommaInTo()
952     {
953         $cachedMessage = $this->messageTestHelper('mail_to_comma.eml', 'text/comma');
954         $completeMessage = $this->_controller->getCompleteMessage($cachedMessage);
955         
956         $this->assertEquals('inscription@arrakeen.net', $completeMessage->to[0]);
957         $this->assertEquals('November 2010 Crystal Newsletter - Cut the Rope Update Released!', $completeMessage->subject);
958     }
959     
960     /**
961      * testUnparseableMail
962      */
963     public function testUnparseableMail()
964     {
965         $cachedMessage = $this->messageTestHelper('unparseable.eml', 'multipart/unparseable');
966         $completeMessage = $this->_controller->getCompleteMessage($cachedMessage);
967         
968         $this->assertEquals(1, preg_match('@NIL|Content-Type: image/jpeg@', $completeMessage->body), 'parsed mail body:' . $completeMessage->body);
969     }
970     
971     /**
972      * test utf8 header decode
973      */
974     public function testUtf8HeaderDecode()
975     {
976         $cachedMessage = $this->messageTestHelper('decode_utf8_header.eml');
977         $completeMessage = $this->_controller->getCompleteMessage($cachedMessage);
978         $this->assertEquals('"Jörn Meier" <j.meier@test.local>', $completeMessage->headers['reply-to']);
979         $this->assertEquals('Jörn Meier <j.meier@test.local>', $completeMessage->headers['from']);
980         $this->assertEquals('j.meier@test.local', $completeMessage->to[0]);
981     }
982     
983     /**
984      * testLongFrom
985      */
986     public function testLongFrom()
987     {
988         $cachedMessage = $this->messageTestHelper('longfrom.eml');
989         $this->assertEquals('nDqIxSoSTIC', $cachedMessage->subject);
990     }
991     
992     /**
993      * testGetMessageWithQuotedPrintableDecodeProblem
994      */
995     public function testGetMessageWithQuotedPrintableDecodeProblem()
996     {
997         $cachedMessage = $this->messageTestHelper('Terminbestaetigung.eml', 'Terminbestaetigung.eml');
998         $completeMessage = $this->_controller->getCompleteMessage($cachedMessage);
999         
1000         $this->assertContains('Veröffentlichungen, Prospekte und Ähnliches bereithalten würden.', $completeMessage->body);
1001     }
1002     
1003     /**
1004      * test move to another account
1005      * 
1006      * @group longrunning
1007      */
1008     public function testMoveMessageToAnotherAccount()
1009     {
1010         $clonedAccount = $this->_cloneAccount();
1011         $folder = $this->getFolder('INBOX', $clonedAccount);
1012         
1013         $cachedMessage = $this->messageTestHelper('multipart_mixed.eml', 'multipart/mixed');
1014         $this->_moveTestHelper($cachedMessage, $folder);
1015     }
1016     
1017     /**
1018      * test move to another account (with message filter)
1019      * 
1020      * @group longrunning
1021      */
1022     public function testMoveMessageToAnotherAccountWithFilter()
1023     {
1024         $clonedAccount = $this->_cloneAccount();
1025         $folder = $this->getFolder('INBOX', $clonedAccount);
1026         
1027         $cachedMessage = $this->messageTestHelper('multipart_mixed.eml', 'multipart/mixed');
1028         $messageFilter = new Felamimail_Model_MessageFilter(array(
1029             array('field' => 'id', 'operator' => 'in', 'value' => array($cachedMessage->getId()))
1030         ));
1031         
1032         $this->_moveTestHelper($messageFilter, $folder);
1033     }
1034     
1035     /**
1036      * move message test helper
1037      *
1038      * @param mixed $_toMove
1039      * @param Felamimail_Model_Folder $_folder
1040      */
1041     protected function _moveTestHelper($_toMove, $_folder)
1042     {
1043         Felamimail_Controller_Message_Move::getInstance()->moveMessages($_toMove, $_folder);
1044         $message = $this->_searchMessage('multipart/mixed', $_folder);
1045         
1046         $folder = $this->_cache->updateCache($_folder, 30);
1047         while ($folder->cache_status === Felamimail_Model_Folder::CACHE_STATUS_INCOMPLETE) {
1048             $folder = $this->_cache->updateCache($folder, 30);
1049         }
1050         $result = $this->_controller->search($this->_getFilter($folder->getId()));
1051         foreach ($result as $messageInCache) {
1052             if ($messageInCache->messageuid == $message['uid']) {
1053                 $foundMessage = $messageInCache;
1054                 break;
1055             }
1056         }
1057         
1058         $this->assertTrue(isset($foundMessage));
1059         $this->_createdMessages[] = $foundMessage;
1060         $completeMessage = $this->_controller->getCompleteMessage($foundMessage);
1061         $this->assertContains('The attached list notes all of the packages that were added or removed', $completeMessage->body);
1062     }
1063     
1064      /**
1065      * test delete in different accounts
1066      * 
1067      * @group longrunning
1068      */
1069     public function testDeleteMessagesInDifferentAccounts()
1070     {
1071         $clonedAccount = $this->_cloneAccount();
1072         
1073         $trashFolderMainAccount = $this->getFolder('Trash');
1074         $trashFolderClonedAccount = $this->getFolder('Trash', $clonedAccount);
1075         
1076         // empty trash
1077         Felamimail_Controller_Folder::getInstance()->emptyFolder($trashFolderMainAccount);
1078         
1079         $cachedMessage1 = $this->messageTestHelper('multipart_mixed.eml', 'multipart/mixed', $trashFolderMainAccount);
1080         $cachedMessage2 = $this->messageTestHelper('complete.eml', 'text/service', $trashFolderClonedAccount);
1081         
1082         Felamimail_Controller_Message_Flags::getInstance()->addFlags(array($cachedMessage1->getId(), $cachedMessage2->getId()), array(Zend_Mail_Storage::FLAG_DELETED));
1083         
1084         $result1 = $this->_searchOnImap('multipart/mixed', $trashFolderMainAccount);
1085         $this->assertEquals(0, count($result1), $trashFolderMainAccount->globalname . ' still contains multipart/mixed messages:' . print_r($result1, TRUE));
1086         $result2 = $this->_searchOnImap('text/service', $trashFolderClonedAccount);
1087         $this->assertEquals(0, count($result2), $trashFolderClonedAccount->globalname . ' still contains text/service messages:' . print_r($result2, TRUE));
1088     }
1089     
1090     /**
1091      * test converting from punycode (xn--stermnn-9wa0n.org -> östermänn.org)
1092      */
1093     public function testPunycodedFromHeader()
1094     {
1095         $cachedMessage = $this->messageTestHelper('punycode_from.eml', 'punycode');
1096         $this->assertEquals('albert@östermänn.org', $cachedMessage->from_email);
1097     }
1098
1099     /**
1100      * test converting to punycode
1101      */
1102     public function testEncodeToPunycode()
1103     {
1104         $message = new Felamimail_Model_Message(array(
1105             'to'        => array('albert@östermänn.org'),
1106             'subject'   => 'punycode test',
1107         ));
1108         $mail = Felamimail_Controller_Message_Send::getInstance()->createMailForSending($message, $this->_account);
1109         
1110         $recipients = $mail->getRecipients();
1111         $this->assertEquals('albert@xn--stermnn-9wa0n.org', $recipients[0]);
1112     }
1113     
1114     /**
1115      * test line end encoding of Zend_Mime_Part / Smtp Protocol
1116      */
1117     public function testSendWithWrongLineEnd()
1118     {
1119         $config = TestServer::getInstance()->getConfig();
1120         $mailDomain = ($config->maildomain) ? $config->maildomain : 'tine20.org';
1121         
1122         // build message with wrong line end rfc822 part
1123         $mail = new Tinebase_Mail('utf-8');
1124         $mail->setBodyText('testmail' . "\r\n" . "\r\n");
1125         $mail->setFrom('unittest@' . $mailDomain, 'unittest');
1126         $mail->setSubject('line end test');
1127         $mail->addTo('unittest@' . $mailDomain);
1128         $mail->addHeader('X-Tine20TestMessage', 'lineend');
1129         
1130         // replace EOLs
1131         $content = file_get_contents(dirname(dirname(__FILE__)) . '/files/text_plain.eml');
1132         $content = preg_replace("/\\x0a/", "\r\n", $content);
1133         $stream = fopen("php://temp", 'r+');
1134         fputs($stream, $content);
1135         rewind($stream);
1136         
1137         $attachment = new Zend_Mime_Part($stream);
1138         $attachment->type        = Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822;
1139         $attachment->encoding    =  null;
1140         $attachment->charset     = 'ISO-8859-1';
1141         $attachment->filename    = 'attach.eml';
1142         $attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT;
1143         
1144         $mail->addAttachment($attachment);
1145         
1146         $smtpConfig = $this->_account->getSmtpConfig();
1147         $transport = new Felamimail_Transport($smtpConfig['hostname'], $smtpConfig);
1148         Zend_Mail_Protocol_Abstract::$loggingEnabled = true;
1149         $mail->send($transport);
1150         Zend_Mail_Protocol_Abstract::$loggingEnabled = false;
1151         
1152         $smtpLog = $transport->getConnection()->getLog();
1153         
1154         $badLineEndCount = preg_match_all("/\\x0d\\x0d\\x0a/", $smtpLog, $matches);
1155         $this->assertEquals(0, $badLineEndCount);
1156         
1157         $badLineEndCount = preg_match_all("/\\x0d/", $smtpLog, $matches);
1158         $this->assertTrue(preg_match_all("/\\x0d/", $smtpLog, $matches) > 70, 'unix line ends are missing');
1159         
1160     }
1161     
1162    /**
1163     * validate email invitation
1164     */
1165     public function testEmailInvitation()
1166     {
1167         $email = $this->_getTestEmailAddress();
1168         $cachedMessage = $this->messageTestHelper('invitation.eml', NULL, NULL, array('unittest@tine20.org', $email));
1169         $this->_testInvitationMessage($cachedMessage, 'pwulf@tine20.org', 'testevent', 2);
1170     }
1171     
1172     /**
1173      * _testInvitationMessage
1174      * 
1175      * @param Felamimail_Model_Message $cachedMessage
1176      * @param string $expectedOriginator
1177      * @param string $expectedEventSummary
1178      * @param integer $expectedAttendeeCount
1179      */
1180     protected function _testInvitationMessage($cachedMessage, $expectedOriginator, $expectedEventSummary, $expectedAttendeeCount)
1181     {
1182         $message = $this->_controller->getCompleteMessage($cachedMessage);
1183         
1184         $this->assertEquals(1, count($message->preparedParts));
1185         $preparediMIPPart = $message->preparedParts->getFirstRecord()->preparedData;
1186         $this->assertTrue($preparediMIPPart instanceof Calendar_Model_iMIP, 'is no iMIP');
1187         $this->assertEquals($expectedOriginator, $preparediMIPPart->originator);
1188         $event = $preparediMIPPart->getEvent();
1189         $this->assertTrue($event instanceof Calendar_Model_Event, 'is no event');
1190         $this->assertEquals($expectedEventSummary, $event->summary);
1191         $this->assertEquals($expectedAttendeeCount, count($event->attendee));
1192     }
1193
1194    /**
1195     * validate email invitation from mac
1196     */
1197     public function testEmailInvitationFromMac()
1198     {
1199         $cachedMessage = $this->messageTestHelper('mac_invitation.eml');
1200     
1201         $message = $this->_controller->getCompleteMessage($cachedMessage);
1202     
1203         $this->assertEquals(1, count($message->preparedParts));
1204         $preparediMIPPart = $message->preparedParts->getFirstRecord()->preparedData;
1205         $this->assertTrue($preparediMIPPart instanceof Calendar_Model_iMIP, 'is no iMIP');
1206         $this->assertEquals('pwulf@tine20.org', $preparediMIPPart->originator);
1207     }
1208
1209    /**
1210     * validate email invitation from outlook
1211     * 
1212     * @see 0006110: handle iMIP messages from outlook
1213     */
1214     public function testEmailInvitationFromOutlook()
1215     {
1216         $email = $this->_getTestEmailAddress();
1217         $cachedMessage = $this->messageTestHelper('outlookimip.eml', NULL, NULL, array('name@example.net', $email));
1218         $this->_testInvitationMessage($cachedMessage, 'name@example.com', 'test', 1);
1219     }
1220     
1221    /**
1222     * validate email invitation from outlook (base64 encoded ics)
1223     * 
1224     * @see 0006110: handle iMIP messages from outlook
1225     */
1226     public function testEmailInvitationFromOutlookBase64()
1227     {
1228         $email = $this->_getTestEmailAddress();
1229         $cachedMessage = $this->messageTestHelper('invite_outlook.eml', NULL, NULL, array('oliver@example.org', $email));
1230         $this->_testInvitationMessage($cachedMessage, 'user@telekom.ch', 'Test von Outlook an Tine20', 1);
1231     }
1232     
1233     /**
1234      * get test email address
1235      * 
1236      * @return string
1237      */
1238     protected function _getTestEmailAddress()
1239     {
1240         $testConfig = Zend_Registry::get('testConfig');
1241         $email = ($testConfig->email) ? $testConfig->email : 'unittest@tine20.org';
1242         return $email;
1243     }
1244     
1245     /**
1246      * testFromUTF8Encoding
1247      * 
1248      * @see 0006538: charset problems with recipients/senders
1249      */
1250     public function testFromUTF8Encoding()
1251     {
1252         $cachedMessage = $this->messageTestHelper('UTF8inFrom.eml');
1253         $this->assertEquals('Philipp Schüle', $cachedMessage->from_name, print_r($cachedMessage->toArray(), TRUE));
1254     }
1255     
1256     /**
1257      * testHeaderWithoutEncodingInformation
1258      * 
1259      * @see 0006250: missing Umlauts in some mails
1260      */
1261     public function testHeaderWithoutEncodingInformation()
1262     {
1263         $cachedMessage = $this->messageTestHelper('Wortmann1.eml');
1264         
1265         $this->assertTrue(! empty($cachedMessage->subject) && is_string($cachedMessage->subject), 'subject empty or no string: '. print_r($cachedMessage->toArray(), TRUE));
1266         $this->assertContains('Höchstgeschwindigkeit', $cachedMessage->subject, print_r($cachedMessage->toArray(), TRUE));
1267     }
1268     
1269     /**
1270      * testFilterTooMuchHtml
1271      * 
1272      * @see 0007142: sometimes we filter to much html content
1273      */
1274     public function testFilterTooMuchHtml()
1275     {
1276         $cachedMessage = $this->messageTestHelper('heavyhtml.eml');
1277         $message = $this->_controller->getCompleteMessage($cachedMessage);
1278         
1279         $this->assertContains('unwahrscheinlichen Fall, dass Probleme auftreten sollten,', $message->body, print_r($message->toArray(), TRUE));
1280     }
1281     
1282     /**
1283      * testUmlautAttachment
1284      * 
1285      * @see 0007624: losing umlauts in attached filenames
1286      */
1287     public function testUmlautAttachment()
1288     {
1289         $cachedMessage = $this->messageTestHelper('attachmentUmlaut.eml');
1290         $message = $this->_controller->getCompleteMessage($cachedMessage);
1291         
1292         $this->assertEquals(1, count($message->attachments));
1293         $this->assertEquals('äöppopä.txt', $message->attachments[0]['filename']);
1294     }
1295
1296     /**
1297      * testNewsletterMultipartRelated
1298      * 
1299      * @see 0007722: improve handling of newsletters
1300      */
1301     public function testNewsletterMultipartRelated()
1302     {
1303         $cachedMessage = $this->messageTestHelper('mw_newsletter_multipart_related.eml');
1304         $this->assertEquals(1, $cachedMessage->has_attachment);
1305         $bodyParts = $cachedMessage->getBodyParts();
1306         $this->assertEquals(Zend_Mime::TYPE_HTML, $bodyParts['2.1']['contentType'], 'multipart/related html part missing: ' . print_r($bodyParts, TRUE));
1307         
1308         $message = $this->_controller->getCompleteMessage($cachedMessage);
1309         
1310         $this->assertNotContains('----------------------------<br />TINE 2.0<br />-----------------------', $message->body, 'message body contains plain/text part');
1311         $this->assertContains('<p style="color:#999999;"><strong>Die Glühweinzeit hat bereits begonnen und kälter geworden ist es auch...</strong></p>', $message->body);
1312         $this->assertEquals(Zend_Mime::TYPE_HTML, $message->body_content_type);
1313     }
1314
1315     /**
1316      * testNewsletterMultipartRelated
1317      * 
1318      * @see 0007858: could not parse structure of multipart/related msg
1319      */
1320     public function testMultipartRelatedAlternative()
1321     {
1322         $cachedMessage = $this->messageTestHelper('multipart_alternative_related.eml');
1323         $message = $this->_controller->getCompleteMessage($cachedMessage);
1324         $this->assertContains('some body contentsome body contentsome body content', $message->body);
1325     }
1326
1327     /**
1328      * testNoAttachement
1329      * 
1330      * @see 0008014: js client shows wrong attachment icon in grid
1331      */
1332     public function testNoAttachement()
1333     {
1334         $cachedMessage = $this->messageTestHelper('noattachment.eml');
1335         $this->assertEquals(0, $cachedMessage->has_attachment);
1336     }
1337     
1338     /**
1339      * testHtmlPurify
1340      * 
1341      * @see 0007726: show inline images of multipart/related message parts
1342      * 
1343      * @todo allow external resources
1344      */
1345     public function testHtmlPurify()
1346     {
1347         $cachedMessage = $this->messageTestHelper('text_html_urls.eml');
1348         $message = $this->_controller->getCompleteMessage($cachedMessage);
1349         
1350 //         $this->assertContains('<div></div>
1351 //     <img src="http://localhost/tine20/index.php?Felamimail.getResource&amp;uri=aHR0cDovL3d3dy50aW5lMjAub3JnL2ZpbGVhZG1pbi90ZW1wbGF0ZXMvaW1hZ2VzL3RpbmUyMC5wbmc=&amp;type=img" alt="tine20.png" /><img src="http://localhost/tine20.png" alt="tine20.png" />
1352     
1353 //     <p>text</p>', $message->body);
1354         $this->assertContains('<div></div>
1355     <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="w38GIAXDIBKE0DHxgljNBAAO 9TXL0Y4OHwAAAAB" />
1356     
1357     <p>text</p>', $message->body);
1358     }
1359
1360     /**
1361      * testNewsletterMultipartRelated
1362      * 
1363      * this tests if cid scheme src urls are replaced with Felamimail.getResource...
1364      * 
1365      * @see 0007726: show inline images of multipart/related message parts
1366      * 
1367      * @return Felamimail_Model_Message
1368      */
1369     public function testNewsletterMultipartRelatedWithImages()
1370     {
1371         $cachedMessage = $this->messageTestHelper('mw_newsletter_multipart_related.eml');
1372         $message = $this->_controller->getCompleteMessage($cachedMessage);
1373         
1374         $this->assertContains('<img src="index.php?method=Felamimail.getResource&amp;cid=1354533197.50bc894dacd37@www.metaways.de&amp;messageId=' . $message->getId() . '"', $message->body);
1375         
1376         return $message;
1377     }
1378     
1379     /**
1380      * testGetResource
1381      * 
1382      * @see 0007726: show inline images of multipart/related message parts
1383      */
1384     public function testGetResource()
1385     {
1386         $message = $this->testNewsletterMultipartRelatedWithImages();
1387         
1388         $resourcePart = $this->_controller->getResourcePartStructure('1354533197.50bc894dacd37@www.metaways.de', $message->getId());
1389         
1390         $this->assertEquals('2.3', $resourcePart['partId']);
1391     }
1392     
1393     /**
1394      * testFacebookNotification
1395      * 
1396      * @see 0008016: links in facebook/twitter emails are removed
1397      */
1398     public function testFacebookNotification()
1399     {
1400         $cachedMessage = $this->messageTestHelper('facebook_notification.eml');
1401         $message = $this->_controller->getCompleteMessage($cachedMessage);
1402         
1403         $this->assertContains('http://www.facebook.com/n/?notifications&amp;id=295475095891&amp;'
1404             . 'mid=7a0ffadG5af33a8a9c98Ga61c449Gdd&amp;bcode=1.1362559617.Abl6w95TdWQc0VVS&amp;n_m=tine20%40metaways.de', $message->body);
1405     }
1406
1407     /**
1408      * testBlockquoteClass
1409      * 
1410      * @see 0008574: add class "felamimail-body-blockquote" to all blockquote tags in mail body
1411      */
1412     public function testBlockquoteClass()
1413     {
1414         $cachedMessage = $this->messageTestHelper('blockquote.eml');
1415         $message = $this->_controller->getCompleteMessage($cachedMessage);
1416         
1417         $this->assertNotContains('<blockquote>', $message->body);
1418     }
1419     
1420     /********************************* protected helper funcs *************************************/
1421     
1422     /**
1423      * clones the account
1424      *
1425      * @return Felamimail_Model_Account
1426      */
1427     protected function _cloneAccount()
1428     {
1429         $account = clone($this->_account);
1430         unset($account->id);
1431         $this->_accountsToDelete[] = $account;
1432         $account = Felamimail_Controller_Account::getInstance()->create($account);
1433         
1434         return $account;
1435     }
1436     
1437     /**
1438      * helper function
1439      * - appends message from file
1440      * - adds appended message to cache
1441      *
1442      * @param string $_filename
1443      * @param string $_testHeaderValue
1444      * @param Felamimail_Model_Folder $_folder
1445      * @param array $_replacements
1446      * @return Felamimail_Model_Message
1447      */
1448     public function messageTestHelper($_filename, $_testHeaderValue = NULL, $_folder = NULL, $_replacements = array())
1449     {
1450         $testHeaderValue = ($_testHeaderValue !== NULL) ? $_testHeaderValue : $_filename;
1451         $folder = ($_folder !== NULL) ? $_folder : $this->_folder;
1452         $this->_appendMessage($_filename, $folder, $_replacements);
1453         return $this->searchAndCacheMessage($testHeaderValue, $folder);
1454     }
1455     
1456     /**
1457      * search message by header (X-Tine20TestMessage) and add it to cache
1458      *
1459      * @param string $_testHeaderValue
1460      * @param Felamimail_Model_Folder $_folder
1461      * @param boolean $assert
1462      * @param string $testHeader
1463      * @return Felamimail_Model_Message|NULL
1464      */
1465     public function searchAndCacheMessage($_testHeaderValue, $_folder = NULL, $assert = TRUE, $testHeader = 'X-Tine20TestMessage')
1466     {
1467         $folder = ($_folder !== NULL) ? $_folder : $this->_folder;
1468         $message = $this->_searchMessage($_testHeaderValue, $folder, $assert, $testHeader);
1469         
1470         if ($message === NULL && ! $assert) {
1471             return NULL;
1472         }
1473         
1474         $cachedMessage = $this->_cache->addMessage($message, $folder);
1475         if ($cachedMessage === FALSE) {
1476             // try to add message again (it had a duplicate)
1477             $this->_cache->clear($folder);
1478             $cachedMessage = $this->_cache->addMessage($message, $folder);
1479         }
1480         
1481         if ($assert) {
1482             $this->assertTrue($cachedMessage instanceof Felamimail_Model_Message, 'could not add message to cache');
1483         }
1484         
1485         $this->_createdMessages->addRecord($cachedMessage);
1486         
1487         return $cachedMessage;
1488     }
1489     
1490     /**
1491      * search message in folder
1492      *
1493      * @param string $_testHeaderValue
1494      * @param Felamimail_Model_Folder $_folder
1495      * @param boolean $_assert
1496      * @param string $testHeader
1497      * @return array|NULL
1498      */
1499     protected function _searchMessage($_testHeaderValue, $_folder, $_assert = TRUE, $testHeader = 'X-Tine20TestMessage')
1500     {
1501         $imap = $this->_getImapFromFolder($_folder);
1502         
1503         $count = 0;
1504         
1505         $result = $this->_searchOnImap($_testHeaderValue, $_folder, $imap, $testHeader);
1506         
1507         while (count($result) === 0 && $count++ < 5) {
1508             sleep(1);
1509             $result = $this->_searchOnImap($_testHeaderValue, $_folder, $imap, $testHeader);
1510         };
1511         
1512         if ($_assert) {
1513             $this->assertGreaterThan(0, count($result), 'No messages with HEADER ' . $testHeader . ': ' . $_testHeaderValue . ' in folder ' . $_folder->globalname . ' found.');
1514         }
1515         $message = (! empty($result)) ? $imap->getSummary($result[0]) : NULL;
1516         
1517         return $message;
1518     }
1519     
1520     /**
1521      * get imap backend
1522      *
1523      * @param Felamimail_Model_Folder $_folder
1524      * @return Felamimail_Backend_ImapProxy
1525      */
1526     protected function _getImapFromFolder($_folder) {
1527         if ($_folder->account_id == $this->_account->getId()) {
1528             $imap = $this->_imap;
1529         } else {
1530             $imap = Felamimail_Backend_ImapFactory::factory($_folder->account_id);
1531         }
1532         
1533         return $imap;
1534     }
1535     
1536     /**
1537      * search for messages on imap server
1538      *
1539      * @param string $_testHeaderValue
1540      * @param Felamimail_Model_Folder $_folder
1541      * @param Felamimail_Backend_ImapProxy $_imap
1542      * @param string $testHeader
1543      * @return array
1544      */
1545     protected function _searchOnImap($_testHeaderValue, $_folder, $_imap = NULL, $testHeader = 'X-Tine20TestMessage')
1546     {
1547         if ($_imap === NULL) {
1548             $imap = $this->_getImapFromFolder($_folder);
1549         } else {
1550             $imap = $_imap;
1551         }
1552         
1553         $imap->expunge($_folder->globalname);
1554         $result = $imap->search(array(
1555             'HEADER ' . $testHeader . ' ' . $_testHeaderValue
1556         ));
1557         
1558         return $result;
1559     }
1560     
1561     /**
1562      * append message (from given filename) to cache
1563      *
1564      * @param string $_filename
1565      * @param string $_folder
1566      * @param array $_replacements
1567      */
1568     protected function _appendMessage($_filename, $_folder, $_replacements = array())
1569     {
1570         $filename = dirname(dirname(__FILE__)) . '/files/' . $_filename;
1571         if (! empty($_replacements)) {
1572             $message = file_get_contents($filename);
1573             $message = preg_replace('/' . preg_quote($_replacements[0], '/') . '/m', $_replacements[1], $message);
1574         } else {
1575             $message = fopen($filename, 'r');
1576         }
1577         $this->_controller->appendMessage($_folder, $message);
1578     }
1579     
1580     /**
1581      * get message filter
1582      *
1583      * @param string $_folderId
1584      * @return Felamimail_Model_MessageFilter
1585      */
1586     protected function _getFilter($_folderId)
1587     {
1588         return new Felamimail_Model_MessageFilter(array(
1589             array('field' => 'folder_id', 'operator' => 'equals', 'value' => $_folderId)
1590         ));
1591     }
1592     
1593     /**
1594      * get folder
1595      *
1596      * @return Felamimail_Model_Folder
1597      */
1598     public function getFolder($_folderName = null, $_account = NULL)
1599     {
1600         $folderName = ($_folderName !== null) ? $_folderName : $this->_testFolderName;
1601         $account = ($_account !== NULL) ? $_account : $this->_account;
1602         
1603         $filter = new Felamimail_Model_FolderFilter(array(
1604             array('field' => 'globalname', 'operator' => 'equals', 'value' => '',),
1605             array('field' => 'account_id', 'operator' => 'equals', 'value' => $account->getId())
1606         ));
1607         $result = Felamimail_Controller_Folder::getInstance()->search($filter);
1608         $folder = $result->filter('localname', $folderName)->getFirstRecord();
1609         if (empty($folder)) {
1610             $folder = Felamimail_Controller_Folder::getInstance()->create($account, $_folderName);
1611         }
1612
1613         return $folder;
1614     }
1615 }