Merge branch '2015.11' into 2015.11-develop
[tine20] / tests / tine20 / Felamimail / Frontend / JsonTest.php
1 <?php
2
3 use Sabre\DAV;
4
5 /**
6  * Tine 2.0 - http://www.tine20.org
7  *
8  * @package     Felamimail
9  * @license     http://www.gnu.org/licenses/agpl.html
10  * @copyright   Copyright (c) 2009-2014 Metaways Infosystems GmbH (http://www.metaways.de)
11  * @author      Philipp Schüle <p.schuele@metaways.de>
12  */
13
14 /**
15  * Test helper
16  */
17 require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
18
19 /**
20  * Test class for Tinebase_Group
21  */
22 class Felamimail_Frontend_JsonTest extends TestCase
23 {
24     /**
25      * @var Felamimail_Frontend_Json
26      */
27     protected $_json = array();
28
29     /**
30      * message ids to delete
31      *
32      * @var array
33      */
34     protected $_messageIds = array();
35     
36     /**
37      * @var Felamimail_Model_Account
38      */
39     protected $_account = NULL;
40     
41     /**
42      * imap backend
43
44      * @var Felamimail_Backend_ImapProxy
45      */
46     protected $_imap = NULL;
47     
48     /**
49      * name of the folder to use for tests
50      * @var string
51      */
52     protected $_testFolderName = 'Junk';
53     
54     /**
55      * folders to delete in tearDown()
56      * 
57      * @var array
58      */
59     protected $_createdFolders = array();
60
61     /**
62      * are there messages to delete?
63      * 
64      * @var array
65      */
66     protected $_foldersToClear = array();
67
68     /**
69      * active sieve script name to be restored
70      *
71      * @var string
72      */
73     protected $_oldActiveSieveScriptName = NULL;
74
75     /**
76      * was sieve_vacation_active ?
77      *
78      * @var boolean
79      */
80     protected $_oldSieveVacationActiveState = FALSE;
81     
82     /**
83      * old sieve data
84      *
85      * @var Felamimail_Sieve_Backend_Sql
86      */
87     protected $_oldSieveData = NULL;
88
89     /**
90      * sieve script name to delete
91      *
92      * @var string
93      */
94     protected $_testSieveScriptName = NULL;
95
96     /**
97      * sieve vacation template file name
98      *
99      * @var string
100      */
101     protected $_sieveVacationTemplateFile = 'vacation_template.tpl';
102
103     /**
104      * test email domain
105      *
106      * @var string
107      */
108     protected $_mailDomain = 'tine20.org';
109
110     /**
111      * @var Felamimail_Model_Folder
112      */
113     protected $_folder = NULL;
114
115     /**
116      * paths in the vfs to delete
117      *
118      * @var array
119      */
120     protected $_pathsToDelete = array();
121
122     /**
123      *
124      * @var Tinebase_Frontend_Json
125      */
126     protected $_frontend = NULL;
127     
128     /**
129      * Sets up the fixture.
130      * This method is called before a test is executed.
131      *
132      * @access protected
133      */
134     protected function setUp()
135     {
136         Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
137         
138         // get (or create) test accout
139         $this->_account = Felamimail_Controller_Account::getInstance()->search()->getFirstRecord();
140         if ($this->_account === null) {
141             $this->markTestSkipped('no account found');
142         }
143         $this->_oldSieveVacationActiveState = $this->_account->sieve_vacation_active;
144         try {
145             $this->_oldSieveData = new Felamimail_Sieve_Backend_Sql($this->_account);
146         } catch (Tinebase_Exception_NotFound $tenf) {
147             // do nothing
148         }
149         
150         $this->_json = new Felamimail_Frontend_Json();
151         $this->_imap = Felamimail_Backend_ImapFactory::factory($this->_account);
152         
153         foreach (array($this->_testFolderName, $this->_account->sent_folder, $this->_account->trash_folder) as $folderToCreate) {
154             // create folder if it does not exist
155             $this->_getFolder($folderToCreate);
156         }
157         
158         $this->_mailDomain = TestServer::getPrimaryMailDomain();
159
160         $this->_frontend = new Tinebase_Frontend_Json();
161     }
162
163     /**
164      * Tears down the fixture
165      * This method is called after a test is executed.
166      *
167      * @access protected
168      */
169     protected function tearDown()
170     {
171         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
172             . ' Tearing down ...');
173         
174         if (count($this->_createdFolders) > 0) {
175             foreach ($this->_createdFolders as $folderName) {
176                 //echo "delete $folderName\n";
177                 try {
178                     $this->_imap->removeFolder(Felamimail_Model_Folder::encodeFolderName($folderName));
179                 } catch (Zend_Mail_Storage_Exception $zmse) {
180                     // already deleted
181                 }
182             }
183             Felamimail_Controller_Cache_Folder::getInstance()->clear($this->_account);
184         }
185         
186         if (! empty($this->_foldersToClear)) {
187             foreach ($this->_foldersToClear as $folderName) {
188                 // delete test messages from given folders on imap server (search by special header)
189                 $this->_imap->selectFolder($folderName);
190                 $result = $this->_imap->search(array(
191                     'HEADER X-Tine20TestMessage jsontest'
192                 ));
193                 //print_r($result);
194                 foreach ($result as $messageUid) {
195                     $this->_imap->removeMessage($messageUid);
196                 }
197                 
198                 // clear message cache
199                 $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $folderName);
200                 Felamimail_Controller_Cache_Message::getInstance()->clear($folder);
201             }
202         }
203         
204         // sieve cleanup
205         if ($this->_testSieveScriptName !== NULL) {
206             Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_testSieveScriptName);
207             try {
208                 Felamimail_Controller_Sieve::getInstance()->deleteScript($this->_account->getId());
209             } catch (Zend_Mail_Protocol_Exception $zmpe) {
210                 // do not delete script if active
211             }
212             Felamimail_Controller_Account::getInstance()->setVacationActive($this->_account, $this->_oldSieveVacationActiveState);
213             
214             if ($this->_oldSieveData !== NULL) {
215                 $this->_oldSieveData->save();
216             }
217         }
218         if ($this->_oldActiveSieveScriptName !== NULL) {
219             Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_oldActiveSieveScriptName);
220             Felamimail_Controller_Sieve::getInstance()->activateScript($this->_account->getId());
221         }
222         
223         // vfs cleanup
224         foreach ($this->_pathsToDelete as $path) {
225             $webdavRoot = new DAV\ObjectTree(new Tinebase_WebDav_Root());
226             //echo "delete $path";
227             $webdavRoot->delete($path);
228         }
229         
230         Tinebase_TransactionManager::getInstance()->rollBack();
231     }
232
233     /************************ test functions *********************************/
234     
235     /*********************** folder tests ****************************/
236     
237     /**
238      * test search folders (check order of folders as well)
239      */
240     public function testSearchFolders()
241     {
242         $filter = $this->_getFolderFilter();
243         $result = $this->_json->searchFolders($filter);
244         
245         $this->assertGreaterThan(1, $result['totalcount']);
246         $expectedFolders = array('INBOX', $this->_testFolderName, $this->_account->trash_folder, $this->_account->sent_folder);
247         
248         $foundCount = 0;
249         foreach ($result['results'] as $index => $folder) {
250             if (in_array($folder['localname'], $expectedFolders)) {
251                 $foundCount++;
252             }
253         }
254         $this->assertEquals(count($expectedFolders), $foundCount);
255     }
256     
257     /**
258      * clear test folder
259      */
260     public function testClearFolder()
261     {
262         $folderName = $this->_testFolderName;
263         $folder = $this->_getFolder($this->_testFolderName);
264         $folder = Felamimail_Controller_Folder::getInstance()->emptyFolder($folder->getId());
265
266         $filter = $this->_getMessageFilter($folder->getId());
267         $result = $this->_json->searchMessages($filter, '');
268         
269         $this->assertEquals(0, $result['totalcount'], 'Found too many messages in folder ' . $this->_testFolderName);
270         $this->assertEquals(0, $folder->cache_totalcount);
271     }
272
273     /**
274      * try to create some folders
275      */
276     public function testCreateFolders()
277     {
278         $filter = $this->_getFolderFilter();
279         $result = $this->_json->searchFolders($filter);
280         
281         $foldernames = array('test' => 'test', 'Schlüssel' => 'Schlüssel', 'test//1' => 'test1', 'test\2' => 'test2');
282         
283         foreach ($foldernames as $foldername => $expected) {
284             $result = $this->_json->addFolder($foldername, $this->_testFolderName, $this->_account->getId());
285             $globalname = $this->_testFolderName . $this->_account->delimiter . $expected;
286             $this->_createdFolders[] = $globalname;
287             $this->assertEquals($expected, $result['localname']);
288             $this->assertEquals($globalname, $result['globalname']);
289             $this->assertEquals(Felamimail_Model_Folder::CACHE_STATUS_EMPTY, $result['cache_status']);
290         }
291     }
292     
293     /**
294      * test emtpy folder (with subfolder)
295      */
296     public function testEmptyFolderWithSubfolder()
297     {
298         $folderName = $this->_testFolderName;
299         $folder = $this->_getFolder($this->_testFolderName);
300         $this->testCreateFolders();
301         
302         $folderArray = $this->_json->emptyFolder($folder->getId());
303         $this->assertEquals(0, $folderArray['has_children']);
304         
305         $result = $this->_json->updateFolderCache($this->_account->getId(), $this->_testFolderName);
306         $this->assertEquals(0, count($result));
307     }
308     
309     /**
310      * testUpdateFolderCache
311      */
312     public function testUpdateFolderCache()
313     {
314         $result = $this->_json->updateFolderCache($this->_account->getId(), '');
315         
316         // create folders directly on imap server
317         $this->_imap->createFolder('test', $this->_testFolderName, $this->_account->delimiter);
318         $this->_imap->createFolder('testsub', $this->_testFolderName . $this->_account->delimiter . 'test', $this->_account->delimiter);
319         // if something goes wrong, we need to delete these folders in tearDown
320         $this->_createdFolders[] = $this->_testFolderName . $this->_account->delimiter . 'test' . $this->_account->delimiter . 'testsub';
321         $this->_createdFolders[] = $this->_testFolderName . $this->_account->delimiter . 'test';
322         
323         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
324             . ' Update cache and check if folder is found');
325         
326         $result = $this->_json->updateFolderCache($this->_account->getId(), $this->_testFolderName);
327         $testfolder = $result[0];
328         $this->assertGreaterThan(0, count($result));
329         $this->assertEquals($this->_testFolderName . $this->_account->delimiter . 'test', $testfolder['globalname']);
330         $this->assertEquals(TRUE, (bool)$testfolder['has_children'], 'should have children');
331         
332         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
333             . ' Delete subfolder directly on imap server');
334         
335         $this->_imap->removeFolder($this->_testFolderName . $this->_account->delimiter . 'test' . $this->_account->delimiter . 'testsub');
336         array_shift($this->_createdFolders);
337         
338         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
339             . ' Check if has_children got updated and folder is removed from cache');
340         
341         $this->_json->updateFolderCache($this->_account->getId(), '');
342         $testfolder = $this->_getFolder($this->_testFolderName . $this->_account->delimiter . 'test');
343         $this->assertEquals(FALSE, (bool)$testfolder['has_children'], 'should have no children');
344
345         return $testfolder;
346     }
347     
348     /**
349      * testUpdateFolderCacheOfNonexistantFolder
350      *
351      * @see 0009800: unselectable folder with subfolders disappears
352      */
353     public function testUpdateFolderCacheOfNonexistantFolder()
354     {
355         $testfolder = $this->testUpdateFolderCache();
356         
357         try {
358             $folderName = $this->_testFolderName . $this->_account->delimiter . 'test' . $this->_account->delimiter . 'testsub';
359             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
360                 . ' Trying to fetch deleted folder ' . $folderName);
361             
362             $testfoldersub = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $folderName);
363             $this->fail('Tinebase_Exception_NotFound expected when looking for folder ' . $folderName);
364         } catch (Tinebase_Exception_NotFound $tenf) {
365         }
366         
367         $this->_imap->removeFolder($this->_testFolderName . $this->_account->delimiter . 'test');
368         array_shift($this->_createdFolders);
369         
370         // try to update message cache of nonexistant folder
371         $removedTestfolder = $this->_json->updateMessageCache($testfolder['id'], 1);
372         $this->assertEquals(0, $removedTestfolder['is_selectable'], 'Folder should not be selectable');
373         
374         // update cache and check if folder is deleted
375         $result = $this->_json->updateFolderCache($this->_account->getId(), $this->_testFolderName);
376         $this->assertEquals(0, count($result));
377     }
378     
379     /*********************** accounts tests **************************/
380     
381     /**
382      * test search for accounts and check default account from config
383      */
384     public function testSearchAccounts()
385     {
386         $system = $this->_getSystemAccount();
387         
388         $this->assertTrue(! empty($system), 'no accounts found');
389         if (TestServer::getInstance()->getConfig()->mailserver) {
390             $this->assertEquals(TestServer::getInstance()->getConfig()->mailserver, $system['host']);
391             $this->assertEquals(TestServer::getInstance()->getConfig()->mailserver, $system['sieve_hostname']);
392         }
393     }
394     
395     /**
396      * get system account
397      *
398      * @return array
399      */
400     protected function _getSystemAccount()
401     {
402         $results = $this->_json->searchAccounts(array());
403         
404         $this->assertGreaterThan(0, $results['totalcount']);
405         $system = array();
406         foreach ($results['results'] as $result) {
407             if ($result['name'] == Tinebase_Core::getUser()->accountLoginName . '@' . $this->_mailDomain) {
408                 $system = $result;
409             }
410         }
411         
412         return $system;
413     }
414     
415     /**
416      * test change / delete of account
417      */
418     public function testChangeDeleteAccount()
419     {
420         $system = $this->_getSystemAccount();
421         unset($system['id']);
422         $system['type'] = Felamimail_Model_Account::TYPE_USER;
423         $account = $this->_json->saveAccount($system);
424         
425         $accountRecord = new Felamimail_Model_Account($account, TRUE);
426         $accountRecord->resolveCredentials(FALSE);
427         if (TestServer::getInstance()->getConfig()->mailserver) {
428             $this->assertEquals(TestServer::getInstance()->getConfig()->mailserver, $account['host']);
429         }
430         
431         $this->_json->changeCredentials($account['id'], $accountRecord->user, 'neuespasswort');
432         $account = $this->_json->getAccount($account['id']);
433         
434         $accountRecord = new Felamimail_Model_Account($account, TRUE);
435         $accountRecord->resolveCredentials(FALSE);
436         $this->assertEquals('neuespasswort', $accountRecord->password);
437         
438         $this->_json->deleteAccounts($account['id']);
439     }
440     
441     /*********************** message tests ****************************/
442     
443     /**
444      * test update message cache
445      */
446     public function testUpdateMessageCache()
447     {
448         $message = $this->_sendMessage();
449         $inbox = $this->_getFolder('INBOX');
450         // update message cache and check result
451         $result = $this->_json->updateMessageCache($inbox['id'], 30);
452         
453         if ($result['cache_status'] == Felamimail_Model_Folder::CACHE_STATUS_COMPLETE) {
454             $this->assertEquals($result['imap_totalcount'], $result['cache_totalcount'], 'totalcounts should be equal');
455         } else if ($result['cache_status'] == Felamimail_Model_Folder::CACHE_STATUS_INCOMPLETE) {
456             $this->assertNotEquals(0, $result['cache_job_actions_est']);
457         }
458     }
459     
460     /**
461      * test folder status
462      */
463     public function testGetFolderStatus()
464     {
465         $filter = $this->_getFolderFilter();
466         $result = $this->_json->searchFolders($filter);
467         $this->assertGreaterThan(1, $result['totalcount']);
468         $expectedFolders = array('INBOX', $this->_testFolderName, $this->_account->trash_folder, $this->_account->sent_folder);
469         
470         foreach ($result['results'] as $folder) {
471             $this->_json->updateMessageCache($folder['id'], 30);
472         }
473         
474         $message = $this->_sendMessage();
475         
476         $status = $this->_json->getFolderStatus(array(array('field' => 'account_id', 'operator' => 'equals', 'value' => $this->_account->getId())));
477         $this->assertEquals(1, count($status));
478         $this->assertEquals($this->_account->sent_folder, $status[0]['localname']);
479     }
480
481     /**
482      * test folder status of deleted folder
483      *
484      * @see 0007134: getFolderStatus should ignore non-existent folders
485      */
486     public function testGetFolderStatusOfDeletedFolder()
487     {
488         $this->testCreateFolders();
489         // remove one of the created folders
490         $removedFolder = $this->_createdFolders[0];
491         $this->_imap->removeFolder(Felamimail_Model_Folder::encodeFolderName($removedFolder));
492         
493         $status = $this->_json->getFolderStatus(array(array('field' => 'account_id', 'operator' => 'equals', 'value' => $this->_account->getId())));
494         $this->assertGreaterThan(2, count($status), 'Expected more than 2 folders that need an update: ' . print_r($status, TRUE));
495         foreach ($status as $folder) {
496             if ($folder['globalname'] == $removedFolder) {
497                 $this->fail('removed folder should not appear in status array!');
498             }
499         }
500     }
501     
502     /**
503      * test send message
504      */
505     public function testSendMessage()
506     {
507         // set email to unittest@tine20.org
508         $contactFilter = new Addressbook_Model_ContactFilter(array(
509             array('field' => 'n_family', 'operator' => 'equals', 'value' => 'Clever')
510         ));
511         $contactIds = Addressbook_Controller_Contact::getInstance()->search($contactFilter, NULL, FALSE, TRUE);
512         $this->assertTrue(count($contactIds) > 0, 'sclever not found in addressbook');
513
514         $contact = Addressbook_Controller_Contact::getInstance()->get($contactIds[0]);
515         $originalEmail =  $contact->email;
516         $contact->email = $this->_account->email;
517         $contact = Addressbook_Controller_Contact::getInstance()->update($contact, FALSE);
518
519         // send email
520         $messageToSend = $this->_getMessageData('unittestalias@' . $this->_mailDomain);
521         $messageToSend['note'] = 1;
522         $messageToSend['bcc']  = array(Tinebase_Core::getUser()->accountEmailAddress);
523         //print_r($messageToSend);
524         $returned = $this->_json->saveMessage($messageToSend);
525         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
526         
527         // check if message is in sent folder
528         $message = $this->_searchForMessageBySubject($messageToSend['subject'], $this->_account->sent_folder);
529         $this->assertEquals($message['from_email'], $messageToSend['from_email']);
530         $this->assertTrue(isset($message['to'][0]));
531         $this->assertEquals($message['to'][0],      $messageToSend['to'][0], 'recipient not found');
532         $this->assertEquals($message['bcc'][0],     $messageToSend['bcc'][0], 'bcc recipient not found');
533         $this->assertEquals($message['subject'],    $messageToSend['subject']);
534         
535         $this->_checkEmailNote($contact, $messageToSend['subject']);
536         
537         // reset sclevers original email address
538         $contact->email = $originalEmail;
539         Addressbook_Controller_Contact::getInstance()->update($contact, FALSE);
540     }
541     
542     /**
543      * check email note
544      *
545      * @param Addressbook_Model_Contact $contact
546      * @param string $subject
547      */
548     protected function _checkEmailNote($contact, $subject)
549     {
550         // check if email note has been added to contact(s)
551         $contact = Addressbook_Controller_Contact::getInstance()->get($contact->getId());
552         $emailNoteType = Tinebase_Notes::getInstance()->getNoteTypeByName('email');
553         
554         // check / delete notes
555         $emailNotes = new Tinebase_Record_RecordSet('Tinebase_Model_Note');
556         foreach ($contact->notes as $note) {
557             if ($note->note_type_id == $emailNoteType->getId()) {
558                 $this->assertContains($subject, $note->note, 'did not find note subject');
559                 $this->assertEquals(Tinebase_Core::getUser()->getId(), $note->created_by);
560                 $this->assertContains('aaaaaä', $note->note);
561                 $emailNotes->addRecord($note);
562             }
563         }
564         $this->assertGreaterThan(0, $emailNotes->count(), 'no email notes found');
565         Tinebase_Notes::getInstance()->deleteNotes($emailNotes);
566     }
567
568     /**
569      * test send message to invalid recipient
570      */
571     public function testSendMessageToInvalidRecipient($invalidEmail = null, $toField = 'to', $expectedExceptionMessage = 'Recipient address rejected')
572     {
573         $messageToSend = $this->_getMessageData($this->_account->email);
574         if ($invalidEmail === null) {
575             $invalidEmail = 'invaliduser@' . $this->_mailDomain;
576         }
577         if ($toField !== 'to') {
578             $messageToSend['to'] = array(Tinebase_Core::getUser()->accountEmailAddress);
579         }
580         $messageToSend[$toField] = array($invalidEmail);
581
582         $translation = Tinebase_Translation::getTranslation('Felamimail');
583
584         try {
585             $this->_json->saveMessage($messageToSend);
586             $this->fail('Tinebase_Exception_SystemGeneric expected');
587         } catch (Tinebase_Exception_SystemGeneric $tesg) {
588             $this->assertContains('>: ' . $translation->_($expectedExceptionMessage), $tesg->getMessage(),
589                 'exception message did not match: ' . $tesg->getMessage());
590         }
591     }
592
593     /**
594      * test send message to invalid recipients (invalid email addresses)
595      *
596      * @see 0012292: check and show invalid email addresses before sending mail
597      */
598     public function testSendMessageWithInvalidEmails()
599     {
600         $this->testSendMessageToInvalidRecipient('memyselfandi.de', 'to', 'Invalid address format');
601         $this->testSendMessageToInvalidRecipient('ich bins <mymail@ ' . $this->_mailDomain .'>', 'cc', 'Invalid address format');
602         $this->testSendMessageToInvalidRecipient('ich bins nicht <mymail\@' . $this->_mailDomain .'>', 'bcc', 'Invalid address format');
603         $this->testSendMessageToInvalidRecipient('my@mail@' . $this->_mailDomain, 'bcc', 'Invalid address format');
604     }
605
606     /**
607      * try to get a message from imap server (with complete body, attachments, etc)
608      *
609      * @see 0006300: add unique message-id header to new messages (for message-id check)
610      */
611     public function testGetMessage()
612     {
613         $message = $this->_sendMessage();
614         
615         // get complete message
616         $message = $this->_json->getMessage($message['id']);
617         
618         // check
619         $this->assertTrue(isset($message['headers']) && $message['headers']['message-id']);
620         $this->assertContains('@' . $this->_mailDomain, $message['headers']['message-id']);
621         $this->assertGreaterThan(0, preg_match('/aaaaaä/', $message['body']));
622         
623         // delete message on imap server and check if correct exception is thrown when trying to get it
624         $this->_imap->selectFolder('INBOX');
625         $this->_imap->removeMessage($message['messageuid']);
626         Tinebase_Core::getCache()->clean();
627         $this->setExpectedException('Felamimail_Exception_IMAPMessageNotFound');
628         $message = $this->_json->getMessage($message['id']);
629     }
630     
631     /**
632      * try to get a message as plain/text
633      */
634     public function testGetPlainTextMessage()
635     {
636         $accountBackend = new Felamimail_Backend_Account();
637         $message = $this->_sendMessage();
638         
639         // get complete message
640         $this->_account->display_format = Felamimail_Model_Account::DISPLAY_PLAIN;
641         $accountBackend->update($this->_account);
642         $message = $this->_json->getMessage($message['id']);
643         $this->_account->display_format = Felamimail_Model_Account::DISPLAY_HTML;
644         $accountBackend->update($this->_account);
645         
646         // check
647         $this->assertEquals("aaaaaä \n\r\n", $message['body']);
648     }
649     
650     /**
651      * try search for a message with path filter
652      */
653     public function testSearchMessageWithPathFilter()
654     {
655         $sentMessage = $this->_sendMessage();
656         $filter = array(array(
657             'field' => 'path', 'operator' => 'in', 'value' => '/' . $this->_account->getId()
658         ));
659         $result = $this->_json->searchMessages($filter, '');
660         $message = $this->_getMessageFromSearchResult($result, $sentMessage['subject']);
661         $this->assertTrue(! empty($message), 'Sent message not found with account path filter');
662
663         $inbox = $this->_getFolder('INBOX');
664         $filter = array(array(
665             'field' => 'path', 'operator' => 'in', 'value' => '/' . $this->_account->getId() . '/' . $inbox->getId()
666         ));
667         $result = $this->_json->searchMessages($filter, '');
668         $message = $this->_getMessageFromSearchResult($result, $sentMessage['subject']);
669         $this->assertTrue(! empty($message), 'Sent message not found with path filter');
670         foreach ($result['results'] as $mail) {
671             $this->assertEquals($inbox->getId(), $mail['folder_id'], 'message is in wrong folder: ' . print_r($mail, TRUE));
672         }
673     }
674     
675     /**
676      * try search for a message with all inboxes and flags filter
677      */
678     public function testSearchMessageWithAllInboxesFilter()
679     {
680         $sentMessage = $this->_sendMessage();
681         $filter = array(
682             array('field' => 'path',  'operator' => 'in',       'value' => Felamimail_Model_MessageFilter::PATH_ALLINBOXES),
683             array('field' => 'flags', 'operator' => 'notin',    'value' => Zend_Mail_Storage::FLAG_FLAGGED),
684         );
685         $result = $this->_json->searchMessages($filter, '');
686         $this->assertGreaterThan(0, $result['totalcount']);
687         $this->assertEquals($result['totalcount'], count($result['results']));
688         
689         $message = $this->_getMessageFromSearchResult($result, $sentMessage['subject']);
690         $this->assertTrue(! empty($message), 'Sent message not found with all inboxes filter');
691     }
692     
693     /**
694      * try search for a message with three cache filters to force a foreign relation join with at least 2 tables
695      */
696     public function testSearchMessageWithThreeCacheFilter()
697     {
698         $filter = array(
699             array('field' => 'flags',   'operator' => 'in',       'value' => Zend_Mail_Storage::FLAG_ANSWERED),
700             array('field' => 'to',      'operator' => 'contains', 'value' => 'testDOESNOTEXIST'),
701             array('field' => 'subject', 'operator' => 'contains', 'value' => 'testDOESNOTEXIST'),
702         );
703         $result = $this->_json->searchMessages($filter, '');
704         $this->assertEquals(0, $result['totalcount']);
705     }
706     
707     /**
708      * try search for a message with empty path filter
709      */
710     public function testSearchMessageEmptyPath()
711     {
712         $sentMessage = $this->_sendMessage();
713         
714         $filter = array(
715             array('field' => 'path',  'operator' => 'equals',   'value' => ''),
716         );
717         $result = $this->_json->searchMessages($filter, '');
718         
719         $this->assertEquals(0, $result['totalcount']);
720         $accountFilterFound = FALSE;
721         
722         foreach ($result['filter'] as $filter) {
723             if ($filter['field'] === 'account_id' && empty($filter['value'])) {
724                 $accountFilterFound = TRUE;
725                 break;
726             }
727         }
728         $this->assertTrue($accountFilterFound);
729     }
730     
731     /**
732      * test flags (add + clear + deleted)
733      */
734     public function testAddAndClearFlags()
735     {
736         $message = $this->_sendMessage();
737         $inboxBefore = $this->_getFolder('INBOX');
738         
739         $this->_json->addFlags($message['id'], Zend_Mail_Storage::FLAG_SEEN);
740         
741         // check if unread count got decreased
742         $inboxAfter = $this->_getFolder('INBOX');
743         $this->assertTrue($inboxBefore->cache_unreadcount - 1 == $inboxAfter->cache_unreadcount, 'wrong cache unreadcount');
744         
745         $message = $this->_json->getMessage($message['id']);
746         $this->assertTrue(in_array(Zend_Mail_Storage::FLAG_SEEN, $message['flags']), 'seen flag not set');
747         
748         // try with a filter
749         $filter = array(
750             array('field' => 'id', 'operator' => 'in', array($message['id']))
751         );
752         $this->_json->clearFlags($filter, Zend_Mail_Storage::FLAG_SEEN);
753         
754         $message = $this->_json->getMessage($message['id']);
755         $this->assertFalse(in_array(Zend_Mail_Storage::FLAG_SEEN, $message['flags']), 'seen flag should not be set');
756
757         $this->setExpectedException('Tinebase_Exception_NotFound');
758         $this->_json->addFlags(array($message['id']), Zend_Mail_Storage::FLAG_DELETED);
759         $this->_json->getMessage($message['id']);
760     }
761     
762     /**
763      * testMarkFolderRead
764      *
765      * @see 0009812: mark folder as read does not work with pgsql
766      */
767     public function testMarkFolderRead()
768     {
769         $inboxBefore = $this->_getFolder('INBOX');
770         $filter = array(array(
771             'field' => 'folder_id', 'operator' => 'equals', 'value' => $inboxBefore->getId()
772         ), array(
773             'field' => 'flags', 'operator' => 'notin', 'value' => array(Zend_Mail_Storage::FLAG_SEEN)
774         ));
775         $this->_json->addFlags($filter, Zend_Mail_Storage::FLAG_SEEN);
776         
777         $inboxAfter = $this->_getFolder('INBOX');
778         $this->assertEquals(0, $inboxAfter->cache_unreadcount);
779     }
780     
781     /**
782      * test delete from trash
783      */
784     public function testDeleteFromTrashWithFilter()
785     {
786         $message = $this->_sendMessage();
787         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder, $this->_account->trash_folder);
788         
789         $trash = $this->_getFolder($this->_account->trash_folder);
790         $result = $this->_json->moveMessages(array(array(
791             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
792         )), $trash->getId());
793
794         $messageInTrash = $this->_searchForMessageBySubject($message['subject'], $this->_account->trash_folder);
795         
796         // delete messages in trash with filter
797         $this->_json->addFlags(array(array(
798             'field' => 'folder_id', 'operator' => 'equals', 'value' => $trash->getId()
799         ), array(
800             'field' => 'id', 'operator' => 'in', 'value' => array($messageInTrash['id'])
801         )), Zend_Mail_Storage::FLAG_DELETED);
802         
803         $this->setExpectedException('Tinebase_Exception_NotFound');
804         $this->_json->getMessage($messageInTrash['id']);
805     }
806     
807     /**
808      * move message to trash with trash folder constant (Felamimail_Model_Folder::FOLDER_TRASH)
809      */
810     public function testMoveMessagesToTrash()
811     {
812         $message = $this->_sendMessage();
813         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder, $this->_account->trash_folder);
814         
815         $result = $this->_json->moveMessages(array(array(
816             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
817         )), Felamimail_Model_Folder::FOLDER_TRASH);
818
819         $messageInTrash = $this->_searchForMessageBySubject($message['subject'], $this->_account->trash_folder);
820     }
821     
822     /**
823      * test reply mail and check some headers
824      *
825      * @see 0006106: Add References header / https://forge.tine20.org/mantisbt/view.php?id=6106
826      */
827     public function testReplyMessage()
828     {
829         $message = $this->_sendMessage();
830         
831         $replyMessage = $this->_getReply($message);
832         $returned = $this->_json->saveMessage($replyMessage);
833         
834         $result = $this->_getMessages();
835         
836         $replyMessageFound = array();
837         $originalMessage = array();
838         foreach ($result['results'] as $mail) {
839             if ($mail['subject'] == $replyMessage['subject']) {
840                 $replyMessageFound = $mail;
841             }
842             if ($mail['subject'] == $message['subject']) {
843                 $originalMessage = $mail;
844             }
845         }
846         $replyMessageFound = $this->_json->getMessage($replyMessageFound['id']);
847         $originalMessage = $this->_json->getMessage($originalMessage['id']);
848         
849         $this->assertTrue(! empty($replyMessageFound), 'replied message not found');
850         $this->assertTrue(! empty($originalMessage), 'original message not found');
851         
852         // check headers
853         $this->assertTrue(isset($replyMessageFound['headers']['in-reply-to']));
854         $this->assertEquals($originalMessage['headers']['message-id'], $replyMessageFound['headers']['in-reply-to']);
855         $this->assertTrue(isset($replyMessageFound['headers']['references']));
856         $this->assertEquals($originalMessage['headers']['message-id'], $replyMessageFound['headers']['references']);
857         
858         // check answered flag
859         $this->assertTrue(in_array(Zend_Mail_Storage::FLAG_ANSWERED, $originalMessage['flags'], 'could not find flag'));
860     }
861     
862     /**
863      * get reply message data
864      *
865      * @param array $_original
866      * @return array
867      */
868     protected function _getReply($_original)
869     {
870         $replyMessage               = $this->_getMessageData();
871         $replyMessage['subject']    = 'Re: ' . $_original['subject'];
872         $replyMessage['original_id']= $_original['id'];
873         $replyMessage['flags']      = Zend_Mail_Storage::FLAG_ANSWERED;
874         
875         return $replyMessage;
876     }
877
878     /**
879      * test reply mail in sent folder
880      */
881     public function testReplyMessageInSentFolder()
882     {
883         $messageInSent = $this->_sendMessage($this->_account->sent_folder);
884         $replyMessage = $this->_getReply($messageInSent);
885         $returned = $this->_json->saveMessage($replyMessage);
886         
887         $result = $this->_getMessages();
888         $sentMessage = $this->_getMessageFromSearchResult($result, $replyMessage['subject']);
889         $this->assertTrue(! empty($sentMessage));
890     }
891
892     /**
893      * test reply mail with long references header
894      *
895      * @see 0006644: "At least one mail header line is too long"
896      */
897     public function testReplyMessageWithLongHeader()
898     {
899         $messageInSent = $this->_sendMessage($this->_account->sent_folder, array(
900             'references' => '<c95d8187-2c71-437e-adb8-5e1dcdbdc507@email.test.org>
901    <2601bbfa-566e-4490-a3db-aad005733d32@email.test.org>
902    <20120530154350.1854610131@ganymed.de>
903    <7e393ce1-d193-44fc-bf5f-30c61a271fe6@email.test.org>
904    <4FC8B49C.8040704@funk.de>
905    <dba2ad5c-6726-4171-8710-984847c010a1@email.test.org>
906    <20120601123551.5E98610131@ganymed.de>
907    <f1cc3195-8641-46e3-8f20-f60f3e16b107@email.test.org>
908    <20120619093658.37E4210131@ganymed.de>
909    <CA+6Rn2PX2Q3tOk2tCQfCjcaC8zYS5XZX327OoyJfUb+w87vCLQ@mail.net.com>
910    <20120619130652.03DD310131@ganymed.de>
911    <37616c6a-4c47-4b54-9ca6-56875bc9205d@email.test.org>
912    <20120620074843.42E2010131@ganymed.de>
913    <CA+6Rn2MAb2x0qeSfcaW6F=0S7LEQL442Sx2ha9RtwMs4B0esBg@mail.net.com>
914    <20120620092902.88C8C10131@ganymed.de>
915    <c95d8187-2c71-437e-adb8-5e1dcdbdc507@email.test.org>
916    <2601bbfa-566e-4490-a3db-aad005733d32@email.test.org>
917    <20120530154350.1854610131@ganymed.de>
918    <7e393ce1-d193-44fc-bf5f-30c61a271fe6@email.test.org>
919    <4FC8B49C.8040704@funk.de>
920    <dba2ad5c-6726-4171-8710-984847c010a1@email.test.org>
921    <20120601123551.5E98610131@ganymed.de>
922    <f1cc3195-8641-46e3-8f20-f60f3e16b107@email.test.org>
923    <20120619093658.37E4210131@ganymed.de>
924    <CA+6Rn2PX2Q3tOk2tCQfCjcaC8zYS5XZX327OoyJfUb+w87vCLQ@mail.net.com>
925    <20120619130652.03DD310131@ganymed.de>
926    <37616c6a-4c47-4b54-9ca6-56875bc9205d@email.test.org>
927    <20120620074843.42E2010131@ganymed.de>
928    <CA+6Rn2MAb2x0qeSfcaW6F=0S7LEQL442Sx2ha9RtwMs4B0esBg@mail.net.com>
929    <20120620092902.88C8C10131@ganymed.de>'
930         ));
931         $replyMessage = $this->_getReply($messageInSent);
932         $returned = $this->_json->saveMessage($replyMessage);
933         
934         $result = $this->_getMessages();
935         $sentMessage = $this->_getMessageFromSearchResult($result, $replyMessage['subject']);
936         $this->assertTrue(! empty($sentMessage));
937     }
938     
939     /**
940      * test move
941      */
942     public function testMoveMessage()
943     {
944         $message = $this->_sendMessage();
945         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder, $this->_testFolderName);
946         
947         $inbox = $this->_getFolder('INBOX');
948         $inboxBefore = $this->_json->updateMessageCache($inbox['id'], 30);
949         
950         // move
951         $testFolder = $this->_getFolder($this->_testFolderName);
952         $result = $this->_json->moveMessages(array(array(
953             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
954         )), $testFolder->getId());
955
956         // sleep for 2 secs because mailserver may be slower than expected
957         sleep(2);
958
959         $inboxAfter = $this->_getFolder('INBOX');
960         
961         // check if count was decreased correctly
962         $this->assertEquals($inboxBefore['cache_unreadcount'] - 1, $inboxAfter['cache_unreadcount']);
963         $this->assertEquals($inboxBefore['cache_totalcount'] - 1, $inboxAfter['cache_totalcount']);
964         
965         $result = $this->_getMessages($this->_testFolderName);
966         $movedMessage = array();
967         foreach ($result['results'] as $mail) {
968             if ($mail['subject'] == $message['subject']) {
969                 $movedMessage = $mail;
970             }
971         }
972         $this->assertTrue(! empty($movedMessage), 'moved message not found');
973     }
974     
975     /**
976      * forward message test
977      *
978      * @see 0007624: losing umlauts in attached filenames
979      */
980     public function testForwardMessageWithAttachment()
981     {
982         $testFolder = $this->_getFolder($this->_testFolderName);
983         $message = fopen(dirname(__FILE__) . '/../files/multipart_related.eml', 'r');
984         Felamimail_Controller_Message::getInstance()->appendMessage($testFolder, $message);
985         
986         $subject = 'Tine 2.0 bei Metaways - Verbessurngsvorschlag';
987         $message = $this->_searchForMessageBySubject($subject, $this->_testFolderName);
988         
989         $fwdSubject = 'Fwd: ' . $subject;
990         $forwardMessageData = array(
991             'account_id'    => $this->_account->getId(),
992             'subject'       => $fwdSubject,
993             'to'            => array($this->_getEmailAddress()),
994             'body'          => "aaaaaä <br>",
995             'headers'       => array('X-Tine20TestMessage' => 'jsontest'),
996             'original_id'   => $message['id'],
997             'attachments'   => array(new Tinebase_Model_TempFile(array(
998                 'type'  => Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822,
999                 'name'  => 'Verbessurüngsvorschlag',
1000             ), TRUE)),
1001             'flags'         => Zend_Mail_Storage::FLAG_PASSED,
1002         );
1003         
1004         $this->_foldersToClear[] = 'INBOX';
1005         $this->_json->saveMessage($forwardMessageData);
1006         $forwardMessage = $this->_searchForMessageBySubject($fwdSubject);
1007         
1008         // check attachment name
1009         $forwardMessageComplete = $this->_json->getMessage($forwardMessage['id']);
1010         $this->assertEquals(1, count($forwardMessageComplete['attachments']));
1011         $this->assertEquals('Verbessurüngsvorschlag.eml', $forwardMessageComplete['attachments'][0]['filename'], 'umlaut missing from attachment filename');
1012         
1013         $forwardMessage = $this->_json->getMessage($forwardMessage['id']);
1014         $this->assertTrue((isset($forwardMessage['structure']) || array_key_exists('structure', $forwardMessage)), 'structure should be set when fetching complete message: ' . print_r($forwardMessage, TRUE));
1015         $this->assertEquals(Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822, $forwardMessage['structure']['parts'][2]['contentType']);
1016         
1017         $message = $this->_json->getMessage($message['id']);
1018         $this->assertTrue(in_array(Zend_Mail_Storage::FLAG_PASSED, $message['flags']), 'forwarded flag missing in flags: ' . print_r($message, TRUE));
1019     }
1020     
1021     /**
1022      * testSendMessageWithAttachmentWithoutExtension
1023      *
1024      * @see 0008328: email attachment without file extension is not sent properly
1025      */
1026     public function testSendMessageWithAttachmentWithoutExtension()
1027     {
1028         $subject = 'attachment test';
1029         $messageToSend = $this->_getMessageData('unittestalias@' . $this->_mailDomain, $subject);
1030         $tempfileName = 'jsontest' . Tinebase_Record_Abstract::generateUID(10);
1031         $tempfilePath = Tinebase_Core::getTempDir() . DIRECTORY_SEPARATOR . $tempfileName;
1032         file_put_contents($tempfilePath, 'some content');
1033         $tempFile = Tinebase_TempFile::getInstance()->createTempFile($tempfilePath, $tempfileName);
1034         $messageToSend['attachments'] = array(array('tempFile' => array('id' => $tempFile->getId())));
1035         $this->_json->saveMessage($messageToSend);
1036         $forwardMessage = $this->_searchForMessageBySubject($subject);
1037         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
1038         
1039         $fullMessage = $this->_json->getMessage($forwardMessage['id']);
1040         $this->assertTrue(count($fullMessage['attachments']) === 1);
1041         $attachment = $fullMessage['attachments'][0];
1042         $this->assertContains($tempfileName, $attachment['filename'], 'wrong attachment filename: ' . print_r($attachment, TRUE));
1043         $this->assertEquals(16, $attachment['size'], 'wrong attachment size: ' . print_r($attachment, TRUE));
1044     }
1045     
1046     /**
1047      * save message in folder (draft) test
1048      *
1049      * @see 0007178: BCC does not save the draft message
1050      */
1051     public function testSaveMessageInFolder()
1052     {
1053         $messageToSave = $this->_getMessageData();
1054         $messageToSave['bcc'] = array('bccaddress@email.org', 'bccaddress2@email.org');
1055         
1056         $draftsFolder = $this->_getFolder($this->_account->drafts_folder);
1057         $returned = $this->_json->saveMessageInFolder($this->_account->drafts_folder, $messageToSave);
1058         $this->_foldersToClear = array($this->_account->drafts_folder);
1059         
1060         // check if message is in drafts folder and recipients are present
1061         $message = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_account->drafts_folder);
1062         $this->assertEquals($messageToSave['subject'],  $message['subject']);
1063         $this->assertEquals($messageToSave['to'][0],    $message['to'][0], 'recipient not found');
1064         $this->assertEquals(2, count($message['bcc']), 'bcc recipient not found: ' . print_r($message, TRUE));
1065         $this->assertEquals($messageToSave['bcc'][0],   $message['bcc'][0], '1st bcc recipient not found');
1066         $this->assertEquals($messageToSave['bcc'][1],   $message['bcc'][1], '2nd bcc recipient not found');
1067     }
1068     
1069     /**
1070      * testSendReadingConfirmation
1071      *
1072      * @see 0007736: ask user before sending reading confirmation
1073      * @see 0008402: Wrong recipient with read confirmation
1074      */
1075     public function testSendReadingConfirmation()
1076     {
1077         $messageToSave = $this->_getMessageData();
1078         $messageToSave['headers']['disposition-notification-to'] = '"' . Tinebase_Core::getUser()->accountFullName . '" <' . $this->_account->email . '>';
1079         $returned = $this->_json->saveMessageInFolder($this->_testFolderName, $messageToSave);
1080         $messageWithReadingConfirmationHeader = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_testFolderName);
1081         $this->_messageIds[] = $messageWithReadingConfirmationHeader['id'];
1082         $this->_json->sendReadingConfirmation($messageWithReadingConfirmationHeader['id']);
1083         
1084         $translate = Tinebase_Translation::getTranslation('Felamimail');
1085         $subject = $translate->_('Reading Confirmation:') . ' '. $messageToSave['subject'];
1086         $message = $this->_searchForMessageBySubject($subject);
1087         $this->_messageIds[] = $message['id'];
1088         
1089         $complete = $this->_json->getMessage($message['id']);
1090         $this->assertContains($translate->_('Was read by:') . ' ' . $this->_account->from, $complete['body']);
1091     }
1092
1093     /**
1094      * save message in non-existant folder (templates) test
1095      *
1096      * @see 0008476: Drafts are not working
1097      */
1098     public function testSaveMessageInNonExistantTemplatesFolder()
1099     {
1100         $messageToSave = $this->_getMessageData();
1101         
1102         $templatesFolder = $this->_getFolder($this->_account->templates_folder, FALSE);
1103         if ($templatesFolder) {
1104             $this->_json->deleteFolder($templatesFolder['id'], $this->_account->getId());
1105         }
1106         $returned = $this->_json->saveMessageInFolder($this->_account->templates_folder, $messageToSave);
1107         $this->_foldersToClear = array($this->_account->templates_folder);
1108         
1109         // check if message is in templates folder
1110         $message = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_account->templates_folder);
1111         $this->assertEquals($messageToSave['subject'],  $message['subject']);
1112         $this->assertEquals($messageToSave['to'][0],    $message['to'][0], 'recipient not found');
1113     }
1114     
1115     /**
1116      * testSaveMessageNoteWithInvalidChar
1117      *
1118      * @see 0008644: error when sending mail with note (wrong charset)
1119      */
1120     public function testSaveMessageNoteWithInvalidChar()
1121     {
1122         $subject = Tinebase_Core::filterInputForDatabase("\xF0\x9F\x98\x8A\xC2"); // :-) emoji &nbsp;
1123         $messageData = $this->_getMessageData('', $subject);
1124         $messageData['note'] = true;
1125         $messageData['body'] .= "&nbsp;";
1126         
1127         $this->_foldersToClear[] = 'INBOX';
1128         $this->_json->saveMessage($messageData);
1129         $message = $this->_searchForMessageBySubject($subject);
1130         
1131         $contact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
1132         $this->_checkEmailNote($contact, $subject);
1133     }
1134     
1135     /**
1136      * testSaveMessageNoteWithInvalidChar
1137      *
1138      * @see 0008644: error when sending mail with note (wrong charset)
1139      */
1140     public function testSaveMessageWithInvalidChar()
1141     {
1142         $subject = "\xF0\x9F\x98\x8A"; // :-) emoji
1143         $messageData = $this->_getMessageData('', $subject);
1144         $this->_foldersToClear[] = 'INBOX';
1145         $this->_json->saveMessage($messageData);
1146         $message = $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
1147     }
1148     
1149     /**
1150      * testMessageWithInvalidICS
1151      *
1152      * @see 0008786: broken ics causes js error when showing details
1153      */
1154     public function testMessageWithInvalidICS()
1155     {
1156         $inbox = $this->_getFolder('INBOX');
1157         $mailAsString = file_get_contents(dirname(__FILE__) . '/../files/invalidimip.eml');
1158         Felamimail_Controller_Message::getInstance()->appendMessage($inbox, $mailAsString);
1159         
1160         $this->_foldersToClear = array('INBOX');
1161         $message = $this->_searchForMessageBySubject('test invalid imip');
1162         
1163         $fullMessage = $this->_json->getMessage($message['id']);
1164         $this->assertTrue(empty($fullMessage->preparedParts));
1165     }
1166
1167     /**
1168      * testSendMailveopeAPIMessage
1169      *
1170      * - envolpe amored message into PGP MIME structure
1171      */
1172     public function testSendMailveopeAPIMessage()
1173     {
1174         $subject = 'testSendMailveopeAPIMessage';
1175         $messageData = $this->_getMessageData('', $subject);
1176         $messageData['body'] = '-----BEGIN PGP MESSAGE-----
1177 Version: Mailvelope v1.3.3
1178 Comment: https://www.mailvelope.com
1179
1180 wcFMA/0LJF28pDbGAQ//YgtsmEZN+pgIJiBDb7iYwPEOchDRIEjGOx543KF6
1181 5YigW9p39pfcJgvGfT8x9cUIrYGxyw5idPSOEftYXyjjGaOYGaKpRSR4hI83
1182 OcJSlEHKq72xhg04mNpCjjJ8dLBstPcQ7tDtsA8Nfb4PwkUYB9IhIBnARg+n
1183 NvrN8mSA2UnY9ElFCvf30sar8EuM5swAjbk64C8TIypMy/Bg4T93zRdxwik6
1184 7BCcbOpm/2PTsiVYBOTcU4+XdG5eyTENXH58M6UTxTD4/g7Qi5PjN+PxyXqf
1185 v2Y1k9F49Y1egf2QJ2r4PX0EWS8SaynSHiIoBsp1xb07nLwZwCdMPG1QNPpF
1186 l2FqlS4dEuQTdkv0deMvd7gtiNynRTAVcJc1ZC6RuWJ+EH2jA49XWkn14eRC
1187 e5jMtPPudkhubnN9Je5lwatGKbJGyuXh+IaM0E0WQMZ3dm8+ST1l4WpVuGbw
1188 KozLUiTRJP9UoxWOcwpQOnzcSlc4rHmWdtF0y3usM9u9GPREqpNUWkEyEEuv
1189 XdZE7rKKj22dJHLCXxAQEh3m29Y2WVaq50YbtEZ+SwwbrHhxP4+FJEru+byh
1190 fiZ47sVW2KvYGJPvbFoSZHiSvMecxDg8BVwe+naZXww/Rwa/TlaX4bYyzpUG
1191 KeJUAzWEfFpJ0+yAvMGQEC7psIJ9NCx149C4ujiQmajSwhUB3XANcmCGB0wm
1192 JjcqC4AHvc7/t4MrQZm0F/W+nrMmNqbZk+gylVrPs9rFEqu7wbjwTmsFA3sS
1193 LkenvQIxBali6uzCR+nd09REqcYirG9cLti39DW048lhhG/ml+gAxxNEaSpG
1194 NbIoV/3w8n7sAIM1fjuHne8bX0gWG43TTjU8MwSMryG7tCOG5u+Cebh6TAoY
1195 NzbX2dpDhOYq5zXdCgKU4P3eh0csSs4UrqFT3TdAxIGrQJ7KrXvB6+N8gRZo
1196 FcUaR+zrRPJjPUZfi46ecP5SG/tM5ea1hqvkwEnEpqjLmCUxqB+rfxx46USX
1197 hMZd2ukUv6kEKv3EUDsRYu1SlDLhDLhWNx8RJae5XkMR+eUUMyNNVwbeMQbB
1198 VAcMcaPITTk84sH7XElr9eF6sCUN4V79OSBRPGY/aNGrcwcoDSD4Hwu+Lw9w
1199 Q+1n8EQ66gAkbJzCNd5GaYMZR9echkBaD/rdWDS3ktcrMehra+h44MTQONV9
1200 8W+RI+IT5jaIXtB4jePmGjsJjbC9aEhTRBRkUnPA7phgknc52dD74AY/6lzK
1201 yd4uZ6S3vhurJW0Vt4iBWJzhFNiSODh5PzteeNzCVAkGMsQvy1IHk0d3uzcE
1202 0tEuSh8fZOFGB4fvMx9Mk8oAU92wfj4J7AVpSo5oRdxMqAXfaYKqfr2Gn++q
1203 E5LClhVIBbFXclCoe0RYNz4wtxjeeYbP40Bq5g0JvPutD/dBMp8hz8Qt+yyG
1204 d8X4/KmQIXyFZ8aP17GMckE5GVVvY9y89eWnWuTUJdwM540hB/EJNeHHTE5y
1205 N2FSLGcmNkvE+3H7BczQ2ZI1SZDhof+umbUst0qoQW+hHmY3CSma48yGAVox
1206 52u2t7hosHCfpf631Ve/6fcICo8vJ2Qfufu2BGIMlSfx4WzUuaMQBynuxFSa
1207 IbVx8ZTO7dJRKrg72aFmWTf0uNla7vicAhpiLWobyNYcZbIjrAGDfg==
1208 =BaAn
1209 -----END PGP MESSAGE-----';
1210
1211         $this->_foldersToClear[] = 'INBOX';
1212         $this->_json->saveMessage($messageData);
1213
1214         $message = $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
1215         $fullMessage = $this->_json->getMessage($message['id']);
1216
1217         $this->assertContains('multipart/encrypted', $fullMessage['headers']['content-type']);
1218         $this->assertContains('protocol="application/pgp-encrypted"', $fullMessage['headers']['content-type']);
1219         $this->assertCount(2, $fullMessage['structure']['parts']);
1220         $this->assertEquals('application/pgp-encrypted', $fullMessage['structure']['parts'][1]['contentType']);
1221         $this->assertEquals('application/octet-stream', $fullMessage['structure']['parts'][2]['contentType']);
1222
1223         return $fullMessage;
1224     }
1225
1226     /**
1227      * testMessagePGPMime
1228      *
1229      * - prepare amored part of PGP MIME structure
1230      */
1231     public function testMessagePGPMime()
1232     {
1233         $fullMessage = $this->testSendMailveopeAPIMessage();
1234
1235         $this->assertEquals('application/pgp-encrypted', $fullMessage['preparedParts'][0]['contentType']);
1236         $this->assertContains('-----BEGIN PGP MESSAGE-----', $fullMessage['preparedParts'][0]['preparedData']);
1237     }
1238
1239     public function testMessagePGPInline()
1240     {
1241         $inbox = $this->_getFolder('INBOX');
1242         $mailAsString = file_get_contents(dirname(__FILE__) . '/../files/multipart_alternative_pgp_inline.eml');
1243         Felamimail_Controller_Message::getInstance()->appendMessage($inbox, $mailAsString);
1244
1245         $this->_foldersToClear = array('INBOX');
1246         $message = $this->_searchForMessageBySubject('Re: mailvelope und tine20');
1247
1248         $fullMessage = $this->_json->getMessage($message['id']);
1249         $this->assertFalse(empty($fullMessage['preparedParts']));
1250     }
1251
1252     /*********************** sieve tests ****************************/
1253     
1254     /**
1255      * set and get vacation sieve script
1256      *
1257      * @see 0007768: Sieve - Vacation notify frequency not being set (Cyrus)
1258      */
1259     public function testGetSetVacation()
1260     {
1261         $vacationData = $this->_getVacationData();
1262         $this->_sieveTestHelper($vacationData);
1263         
1264         // check if script was activated
1265         $activeScriptName = Felamimail_Controller_Sieve::getInstance()->getActiveScriptName($this->_account->getId());
1266         $this->assertEquals($this->_testSieveScriptName, $activeScriptName);
1267         $updatedAccount = Felamimail_Controller_Account::getInstance()->get($this->_account->getId());
1268         $this->assertTrue((bool) $updatedAccount->sieve_vacation_active);
1269         
1270         $result = $this->_json->getVacation($this->_account->getId());
1271
1272         $this->assertEquals($this->_account->email, $result['addresses'][0]);
1273         
1274         $sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1275         if (preg_match('/dbmail/i', $sieveBackend->getImplementation())) {
1276             $translate = Tinebase_Translation::getTranslation('Felamimail');
1277             $vacationData['subject'] = sprintf($translate->_('Out of Office reply from %1$s'), Tinebase_Core::getUser()->accountFullName);
1278         }
1279         
1280         foreach (array('reason', 'enabled', 'subject', 'from', 'days') as $field) {
1281             $this->assertEquals($vacationData[$field], $result[$field], 'vacation data mismatch: ' . $field);
1282         }
1283     }
1284     
1285     /**
1286      * get vacation data
1287      *
1288      * @return array
1289      */
1290     protected function _getVacationData()
1291     {
1292         return array(
1293             'id'                    => $this->_account->getId(),
1294             'subject'               => 'unittest vacation subject',
1295             'from'                  => $this->_account->from . ' <' . $this->_account->email . '>',
1296             'days'                  => 3,
1297             'enabled'               => TRUE,
1298             'reason'                => 'unittest vacation message<br /><br />signature',
1299             'mime'                  => NULL,
1300         );
1301     }
1302     
1303     /**
1304      * test mime vacation sieve script
1305      */
1306     public function testMimeVacation()
1307     {
1308         $vacationData = $this->_getVacationData();
1309         $vacationData['reason'] = "\n<html><body><h1>unittest vacation&nbsp;message</h1></body></html>";
1310         
1311         $_sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1312         if (! in_array('mime', $_sieveBackend->capability())) {
1313             $vacationData['mime'] = 'text/html';
1314         }
1315         
1316         $this->_sieveTestHelper($vacationData, TRUE);
1317     }
1318     
1319     /**
1320      * test get/set of rules sieve script
1321      */
1322     public function testGetSetRules()
1323     {
1324         $ruleData = $this->_getRuleData();
1325         
1326         $this->_sieveTestHelper($ruleData);
1327         
1328         // check getRules
1329         $result = $this->_json->getRules($this->_account->getId());
1330         $this->assertEquals($result['totalcount'], count($ruleData));
1331         
1332         // check by sending mail
1333         $messageData = $this->_getMessageData('', 'viagra');
1334         $returned = $this->_json->saveMessage($messageData);
1335         $this->_foldersToClear = array('INBOX', $this->_testFolderName);
1336         // check if message is in test folder
1337         $message = $this->_searchForMessageBySubject($messageData['subject'], $this->_testFolderName);
1338     }
1339     
1340     /**
1341      * testRemoveRules
1342      *
1343      * @see 0006490: can not delete single filter rule
1344      */
1345     public function testRemoveRules()
1346     {
1347         $this->testGetSetRules();
1348         $this->_json->saveRules($this->_account->getId(), array());
1349         
1350         $result = $this->_json->getRules($this->_account->getId());
1351         $this->assertEquals(0, $result['totalcount'], 'found rules: ' . print_r($result, TRUE));
1352     }
1353     
1354     /**
1355      * get sieve rule data
1356      *
1357      * @return array
1358      */
1359     protected function _getRuleData()
1360     {
1361         return array(array(
1362             'id'            => 1,
1363             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1364             'action_argument' => $this->_testFolderName,
1365             'conjunction'  => 'allof',
1366             'conditions'    => array(array(
1367                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1368                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1369                 'header'        => 'From',
1370                 'key'           => '"abcd" <info@example.org>',
1371             )),
1372             'enabled'       => 1,
1373         ), array(
1374             'id'            => 2,
1375             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1376             'action_argument' => $this->_testFolderName,
1377             'conjunction'  => 'allof',
1378             'conditions'    => array(array(
1379                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1380                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1381                 'header'        => 'From',
1382                 'key'           => 'info@example.org',
1383             )),
1384             'enabled'       => 0,
1385         ), array(
1386             'id'            => 3,
1387             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1388             'action_argument' => $this->_testFolderName,
1389             'conjunction'  => 'allof',
1390             'conditions'    => array(array(
1391                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_HEADER,
1392                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_REGEX,
1393                 'header'        => 'subject',
1394                 'key'           => '[vV]iagra|cyalis',
1395             )),
1396             'enabled'       => 1,
1397         ));
1398     }
1399     
1400     /**
1401      * test to set a forward rule to this accounts email address
1402      * -> should throw exception to prevent mail cycling
1403      */
1404     public function testSetForwardRuleToSelf()
1405     {
1406         $ruleData = array(array(
1407             'id'            => 1,
1408             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1409             'action_argument' => $this->_account->email,
1410             'conjunction'     => 'allof',
1411             'conditions'    => array(array(
1412                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1413                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1414                 'header'        => 'From',
1415                 'key'           => 'info@example.org',
1416             )),
1417             'enabled'       => 1,
1418         ));
1419         
1420         try {
1421             $this->_sieveTestHelper($ruleData);
1422             $this->assertTrue(FALSE, 'it is not allowed to set own email address for redirect!');
1423         } catch (Felamimail_Exception_Sieve $e) {
1424             $this->assertTrue(TRUE);
1425         }
1426
1427         // this should work
1428         $ruleData[0]['enabled'] = 0;
1429         $this->_sieveTestHelper($ruleData);
1430     }
1431
1432     /**
1433      * @see 0006222: Keep a copy from mails forwarded to another emailaddress
1434      */
1435     public function testSetForwardRuleWithCopy()
1436     {
1437         $ruleData = array(array(
1438             'id'            => 1,
1439             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1440             'action_argument' => array(
1441                 'emails' => 'someaccount@example.org',
1442                 'copy'   => 1,
1443             ),
1444             'conjunction'     => 'allof',
1445             'conditions'    => array(array(
1446                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1447                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1448                 'header'        => 'From',
1449                 'key'           => 'info@example.org',
1450             )),
1451             'enabled'       => 1,
1452         ));
1453
1454         $this->_sieveTestHelper($ruleData);
1455     }
1456
1457     /**
1458      * @see 0006222: Keep a copy from mails forwarded to another emailaddress
1459      */
1460     public function testSetForwardRuleWithoutCopy()
1461     {
1462         $ruleData = array(array(
1463             'id'            => 1,
1464             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1465             'action_argument' => array(
1466                 'emails' => 'someaccount@example.org',
1467                 'copy'   => 0,
1468             ),
1469             'conjunction'     => 'allof',
1470             'conditions'    => array(array(
1471                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1472                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1473                 'header'        => 'From',
1474                 'key'           => 'info@example.org',
1475             )),
1476             'enabled'       => 1,
1477         ));
1478
1479         $this->_sieveTestHelper($ruleData);
1480     }
1481
1482     /**
1483      * testGetVacationTemplates
1484      *
1485      * @return array
1486      */
1487     public function testGetVacationTemplates()
1488     {
1489         $this->markTestSkipped('0010194: fix felamimail webdav tests');
1490         
1491         $this->_addVacationTemplateFile();
1492         $result = $this->_json->getVacationMessageTemplates();
1493         
1494         $this->assertTrue($result['totalcount'] > 0, 'no templates found');
1495         $found = FALSE;
1496         foreach ($result['results'] as $template) {
1497             if ($template['name'] === $this->_sieveVacationTemplateFile) {
1498                 $found = TRUE;
1499                 break;
1500             }
1501         }
1502         
1503         $this->assertTrue($found, 'wrong templates: ' . print_r($result['results'], TRUE));
1504         
1505         return $template;
1506     }
1507     
1508     /**
1509      * add vacation template file to vfs
1510      */
1511     protected function _addVacationTemplateFile()
1512     {
1513         $webdavRoot = new DAV\ObjectTree(new Tinebase_WebDav_Root());
1514         $path = '/webdav/Felamimail/shared/Vacation Templates';
1515         $node = $webdavRoot->getNodeForPath($path);
1516         $this->_pathsToDelete[] = $path . '/' . $this->_sieveVacationTemplateFile;
1517         $node->createFile($this->_sieveVacationTemplateFile, fopen(dirname(__FILE__) . '/../files/' . $this->_sieveVacationTemplateFile, 'r'));
1518     }
1519     
1520     /**
1521      * testGetVacationMessage
1522      */
1523     public function testGetVacationMessage()
1524     {
1525         $this->markTestSkipped('0010194: fix felamimail webdav tests');
1526         
1527         $result = $this->_getVacationMessageWithTemplate();
1528         $sclever = Tinebase_User::getInstance()->getFullUserByLoginName('sclever');
1529         $pwulf = Tinebase_User::getInstance()->getFullUserByLoginName('pwulf');
1530         $this->assertEquals("Ich bin vom 18.04.2012 bis zum 20.04.2012 im Urlaub. Bitte kontaktieren Sie<br /> Paul Wulf (" .
1531             $pwulf->accountEmailAddress . ") oder Susan Clever (" .
1532             $sclever->accountEmailAddress . ").<br /><br />I am on vacation until Apr 20, 2012. Please contact Paul Wulf<br />(" .
1533             $pwulf->accountEmailAddress . ") or Susan Clever (" .
1534             $sclever->accountEmailAddress . ") instead.<br /><br />" .
1535             Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId())->n_fn, $result['message']);
1536     }
1537     
1538     /**
1539      * get vacation message with template
1540      *
1541      * @return array
1542      */
1543     protected function _getVacationMessageWithTemplate()
1544     {
1545         $template = $this->testGetVacationTemplates();
1546         $sclever = Tinebase_User::getInstance()->getFullUserByLoginName('sclever');
1547         $result = $this->_json->getVacationMessage(array(
1548             'start_date' => '2012-04-18',
1549             'end_date'   => '2012-04-20',
1550             'contact_ids' => array(
1551                 Tinebase_User::getInstance()->getFullUserByLoginName('pwulf')->contact_id,
1552                 $sclever->contact_id,
1553             ),
1554             'template_id' => $template['id'],
1555             'signature' => $this->_account->signature
1556         ));
1557         
1558         return $result;
1559     }
1560     
1561     /**
1562      * testGetVacationWithSignature
1563      *
1564      * @see 0006866: check signature linebreaks in vacation message from template
1565      */
1566     public function testGetVacationWithSignature()
1567     {
1568         $this->markTestSkipped('0010194: fix felamimail webdav tests');
1569         
1570         $this->_sieveVacationTemplateFile = 'vacation_template_sig.tpl';
1571         
1572         // set signature with <br> + linebreaks
1573         $this->_account->signature = "llalala<br>\nxyz<br>\nblubb<br>";
1574         
1575         $result = $this->_getVacationMessageWithTemplate();
1576         $this->assertContains('-- <br />llalala<br />xyz<br />blubb<br />', $result['message'], 'wrong linebreaks or missing signature');
1577     }
1578     
1579     /**
1580     * testSetVacationWithStartAndEndDate
1581     *
1582     * @see 0006266: automatic deactivation of vacation message
1583     */
1584     public function testSetVacationWithStartAndEndDate()
1585     {
1586         $vacationData = $this->_getVacationData();
1587         $vacationData['start_date'] = '2012-04-18';
1588         $vacationData['end_date'] = '2012-04-20';
1589         $result = $this->_sieveTestHelper($vacationData);
1590         
1591         $this->assertContains($vacationData['start_date'], $result['start_date']);
1592         $this->assertContains($vacationData['end_date'], $result['end_date']);
1593     }
1594     
1595     /**
1596      * testSieveRulesOrder
1597      *
1598      * @see 0007240: order of sieve rules changes when vacation message is saved
1599      */
1600     public function testSieveRulesOrder()
1601     {
1602         $this->_setTestScriptname();
1603         
1604         // disable vacation first
1605         $this->_setDisabledVacation();
1606         
1607         $sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1608         
1609         $ruleData = $this->_getRuleData();
1610         $ruleData[0]['id'] = $ruleData[2]['id'];
1611         $ruleData[2]['id'] = 11;
1612         $resultSet = $this->_json->saveRules($this->_account->getId(), $ruleData);
1613         $sieveScriptRules = $sieveBackend->getScript($this->_testSieveScriptName);
1614         
1615         $this->_setDisabledVacation();
1616         $sieveScriptVacation = $sieveBackend->getScript($this->_testSieveScriptName);
1617         
1618         // compare sieve scripts
1619         $this->assertContains($sieveScriptRules, $sieveScriptVacation, 'rule order changed');
1620     }
1621     
1622     /**
1623      * use another name for test sieve script
1624      */
1625     protected function _setTestScriptname()
1626     {
1627         $this->_oldActiveSieveScriptName = Felamimail_Controller_Sieve::getInstance()->getActiveScriptName($this->_account->getId());
1628         $this->_testSieveScriptName = 'Felamimail_Unittest';
1629         Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_testSieveScriptName);
1630     }
1631     
1632     /**
1633      * set disabled vacation message
1634      */
1635     protected function _setDisabledVacation()
1636     {
1637         $vacationData = $this->_getVacationData();
1638         $vacationData['enabled'] = FALSE;
1639         $resultSet = $this->_json->saveVacation($vacationData);
1640     }
1641     
1642     /**
1643      * get folder filter
1644      *
1645      * @return array
1646      */
1647     protected function _getFolderFilter()
1648     {
1649         return array(array(
1650             'field' => 'globalname', 'operator' => 'equals', 'value' => ''
1651         ));
1652     }
1653
1654     /**
1655      * get message filter
1656      *
1657      * @param string $_folderId
1658      * @return array
1659      */
1660     protected function _getMessageFilter($_folderId)
1661     {
1662         $result = array(array(
1663             'field' => 'folder_id', 'operator' => 'equals', 'value' => $_folderId
1664         ));
1665         
1666         return $result;
1667     }
1668     
1669     /**
1670      * get mailbox
1671      *
1672      * @param string $name
1673      * @param boolean $createFolder
1674      * @return Felamimail_Model_Folder|NULL
1675      */
1676     protected function _getFolder($name, $createFolder = TRUE)
1677     {
1678         Felamimail_Controller_Cache_Folder::getInstance()->update($this->_account->getId());
1679         try {
1680             $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $name);
1681         } catch (Tinebase_Exception_NotFound $tenf) {
1682             $folder = ($createFolder) ? Felamimail_Controller_Folder::getInstance()->create($this->_account, $name) : NULL;
1683         }
1684         
1685         return $folder;
1686     }
1687
1688     /**
1689      * get message data
1690      *
1691      * @return array
1692      */
1693     protected function _getMessageData($_emailFrom = '', $_subject = 'test')
1694     {
1695         return array(
1696             'account_id'    => $this->_account->getId(),
1697             'subject'       => $_subject,
1698             'to'            => array(Tinebase_Core::getUser()->accountEmailAddress),
1699             'body'          => 'aaaaaä <br>',
1700             'headers'       => array('X-Tine20TestMessage' => 'jsontest'),
1701             'from_email'    => $_emailFrom,
1702             'content_type'  => Felamimail_Model_Message::CONTENT_TYPE_HTML,
1703         );
1704     }
1705
1706     /**
1707      * send message and return message array
1708      *
1709      * @param string $folderName
1710      * @param array $addtionalHeaders
1711      * @return array
1712      */
1713     protected function _sendMessage($folderName = 'INBOX', $addtionalHeaders = array())
1714     {
1715         $messageToSend = $this->_getMessageData();
1716         $messageToSend['headers'] = array_merge($messageToSend['headers'], $addtionalHeaders);
1717         $this->_json->saveMessage($messageToSend);
1718         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
1719
1720         $i = 0;
1721         while ($i < 5) {
1722             $result = $this->_getMessages($folderName);
1723             $message = $this->_getMessageFromSearchResult($result, $messageToSend['subject']);
1724             if (! empty($message)) {
1725                 break;
1726             }
1727             // sleep for 1 sec because mailserver may be slower than expected
1728             sleep(1);
1729             $i++;
1730         }
1731
1732         $this->assertTrue(! empty($message), 'Sent message not found.');
1733         
1734         return $message;
1735     }
1736     
1737     /**
1738      * returns message array from result
1739      *
1740      * @param array $_result
1741      * @param string $_subject
1742      * @return array
1743      */
1744     protected function _getMessageFromSearchResult($_result, $_subject)
1745     {
1746         $message = array();
1747         foreach ($_result['results'] as $mail) {
1748             if ($mail['subject'] == $_subject) {
1749                 $message = $mail;
1750             }
1751         }
1752         
1753         return $message;
1754     }
1755     
1756     /**
1757      * get messages from folder
1758      *
1759      * @param string $_folderName
1760      * @return array
1761      */
1762     protected function _getMessages($_folderName = 'INBOX')
1763     {
1764         $folder = $this->_getFolder($_folderName);
1765         $filter = $this->_getMessageFilter($folder->getId());
1766         // update cache
1767         $folder = Felamimail_Controller_Cache_Message::getInstance()->updateCache($folder, 10, 1);
1768         $i = 0;
1769         while ($folder->cache_status != Felamimail_Model_Folder::CACHE_STATUS_COMPLETE && $i < 10) {
1770             $folder = Felamimail_Controller_Cache_Message::getInstance()->updateCache($folder, 10);
1771             $i++;
1772         }
1773         $result = $this->_json->searchMessages($filter, '');
1774
1775         return $result;
1776     }
1777     
1778     /**
1779      * search for message defined by subject in folder
1780      *
1781      * @param string $_subject
1782      * @param string $_folderName
1783      * @return string message data
1784      */
1785     protected function _searchForMessageBySubject($_subject, $_folderName = 'INBOX')
1786     {
1787         // give server some time to send and receive messages
1788         sleep(1);
1789
1790         $result = $this->_getMessages($_folderName);
1791         
1792         $message = array();
1793         foreach ($result['results'] as $mail) {
1794             if ($mail['subject'] == $_subject) {
1795                 $message = $mail;
1796             }
1797         }
1798         $this->assertGreaterThan(0, $result['totalcount'], 'folder is empty');
1799         $this->assertTrue(! empty($message), 'Message not found');
1800         
1801         return $message;
1802     }
1803     
1804     /**
1805      * sieve test helper
1806      *
1807      * @param array $_sieveData
1808      * @return array
1809      */
1810     protected function _sieveTestHelper($_sieveData, $_isMime = FALSE)
1811     {
1812         $this->_setTestScriptname();
1813         
1814         // check which save fn to use
1815         if ((isset($_sieveData['reason']) || array_key_exists('reason', $_sieveData))) {
1816             $resultSet = $this->_json->saveVacation($_sieveData);
1817             $this->assertEquals($this->_account->email, $resultSet['addresses'][0]);
1818             
1819             $_sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1820             
1821             if (preg_match('/dbmail/i', $_sieveBackend->getImplementation())) {
1822                 $translate = Tinebase_Translation::getTranslation('Felamimail');
1823                 $this->assertEquals(sprintf(
1824                     $translate->_('Out of Office reply from %1$s'), Tinebase_Core::getUser()->accountFullName),
1825                     $resultSet['subject']
1826                 );
1827             } else {
1828                 $this->assertEquals($_sieveData['subject'], $resultSet['subject']);
1829             }
1830             
1831             if ($_isMime) {
1832                 $this->assertEquals(html_entity_decode('unittest vacation&nbsp;message', ENT_NOQUOTES, 'UTF-8'), $resultSet['reason']);
1833             } else {
1834                 $this->assertEquals($_sieveData['reason'], $resultSet['reason']);
1835             }
1836             
1837         } else if ((isset($_sieveData[0]['action_type']) || array_key_exists('action_type', $_sieveData[0]))) {
1838             $resultSet = $this->_json->saveRules($this->_account->getId(), $_sieveData);
1839             $this->assertEquals($_sieveData, $resultSet);
1840         }
1841         
1842         return $resultSet;
1843     }
1844
1845     /**
1846      * search preferences by application felamimail
1847      *
1848      */
1849     public function testSearchFelamimailPreferences()
1850     {
1851         // search prefs
1852         $result = $this->_frontend->searchPreferencesForApplication('Felamimail', '');
1853         
1854         // check results
1855         $this->assertTrue(isset($result['results']));
1856         $this->assertGreaterThan(0, $result['totalcount']);
1857     }
1858     
1859     /**
1860      * testGetRegistryData
1861      *
1862      * @see 0010251: do not send unused config data to client
1863      */
1864     public function testGetRegistryData()
1865     {
1866         $regData = $this->_json->getRegistryData();
1867
1868         $this->assertFalse(isset($regData['defaults']));
1869         $supportedFlags = Felamimail_Config::getInstance()->featureEnabled(Felamimail_Config::FEATURE_TINE20_FLAG)
1870             ? 6
1871             : 5;
1872         $this->assertEquals($supportedFlags, $regData['supportedFlags']['totalcount']);
1873     }
1874 }