improves testSaveMessageInFolder() in test
[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-2017 Metaways Infosystems GmbH (http://www.metaways.de)
11  * @author      Philipp Schüle <p.schuele@metaways.de>
12  */
13
14 /**
15  * Test class for Tinebase_Group
16  */
17 class Felamimail_Frontend_JsonTest extends TestCase
18 {
19     use GetProtectedMethodTrait;
20
21     /**
22      * @var Felamimail_Frontend_Json
23      */
24     protected $_json = array();
25
26     /**
27      * message ids to delete
28      *
29      * @var array
30      */
31     protected $_messageIds = array();
32     
33     /**
34      * @var Felamimail_Model_Account
35      */
36     protected $_account = NULL;
37     
38     /**
39      * imap backend
40
41      * @var Felamimail_Backend_ImapProxy
42      */
43     protected $_imap = NULL;
44     
45     /**
46      * name of the folder to use for tests
47      * @var string
48      */
49     protected $_testFolderName = 'Junk';
50     
51     /**
52      * folders to delete in tearDown()
53      * 
54      * @var array
55      */
56     protected $_createdFolders = array();
57
58     /**
59      * are there messages to delete?
60      * 
61      * @var array
62      */
63     protected $_foldersToClear = array();
64
65     /**
66      * active sieve script name to be restored
67      *
68      * @var string
69      */
70     protected $_oldActiveSieveScriptName = NULL;
71
72     /**
73      * was sieve_vacation_active ?
74      *
75      * @var boolean
76      */
77     protected $_oldSieveVacationActiveState = FALSE;
78     
79     /**
80      * old sieve data
81      *
82      * @var Felamimail_Sieve_Backend_Sql
83      */
84     protected $_oldSieveData = NULL;
85
86     /**
87      * sieve script name to delete
88      *
89      * @var string
90      */
91     protected $_testSieveScriptName = NULL;
92
93     /**
94      * sieve vacation template file name
95      *
96      * @var string
97      */
98     protected $_sieveVacationTemplateFile = 'vacation_template.tpl';
99
100     /**
101      * test email domain
102      *
103      * @var string
104      */
105     protected $_mailDomain = 'tine20.org';
106
107     /**
108      * @var Felamimail_Model_Folder
109      */
110     protected $_folder = NULL;
111
112     /**
113      * paths in the vfs to delete
114      *
115      * @var array
116      */
117     protected $_pathsToDelete = array();
118
119     /**
120      *
121      * @var Tinebase_Frontend_Json
122      */
123     protected $_frontend = NULL;
124     
125     /**
126      * Sets up the fixture.
127      * This method is called before a test is executed.
128      *
129      * @access protected
130      */
131     protected function setUp()
132     {
133         Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
134         
135         // get (or create) test accout
136         $this->_account = Felamimail_Controller_Account::getInstance()->search()->getFirstRecord();
137         if ($this->_account === null) {
138             $this->markTestSkipped('no account found');
139         }
140         $this->_oldSieveVacationActiveState = $this->_account->sieve_vacation_active;
141         try {
142             $this->_oldSieveData = new Felamimail_Sieve_Backend_Sql($this->_account);
143         } catch (Tinebase_Exception_NotFound $tenf) {
144             // do nothing
145         }
146         
147         $this->_json = new Felamimail_Frontend_Json();
148         $this->_imap = Felamimail_Backend_ImapFactory::factory($this->_account);
149         
150         foreach (array($this->_testFolderName, $this->_account->sent_folder, $this->_account->trash_folder) as $folderToCreate) {
151             // create folder if it does not exist
152             $this->_getFolder($folderToCreate);
153         }
154         
155         $this->_mailDomain = TestServer::getPrimaryMailDomain();
156
157         $this->_frontend = new Tinebase_Frontend_Json();
158     }
159
160     /**
161      * Tears down the fixture
162      * This method is called after a test is executed.
163      *
164      * @access protected
165      */
166     protected function tearDown()
167     {
168         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
169             . ' Tearing down ...');
170         
171         if (count($this->_createdFolders) > 0) {
172             foreach ($this->_createdFolders as $folderName) {
173                 //echo "delete $folderName\n";
174                 try {
175                     $this->_imap->removeFolder(Felamimail_Model_Folder::encodeFolderName($folderName));
176                 } catch (Zend_Mail_Storage_Exception $zmse) {
177                     // already deleted
178                 }
179             }
180             Felamimail_Controller_Cache_Folder::getInstance()->clear($this->_account);
181         }
182         
183         if (! empty($this->_foldersToClear)) {
184             foreach ($this->_foldersToClear as $folderName) {
185                 // delete test messages from given folders on imap server (search by special header)
186                 $this->_imap->selectFolder($folderName);
187                 $result = $this->_imap->search(array(
188                     'HEADER X-Tine20TestMessage jsontest'
189                 ));
190                 //print_r($result);
191                 foreach ($result as $messageUid) {
192                     $this->_imap->removeMessage($messageUid);
193                 }
194                 
195                 // clear message cache
196                 $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $folderName);
197                 Felamimail_Controller_Cache_Message::getInstance()->clear($folder);
198             }
199         }
200         
201         // sieve cleanup
202         if ($this->_testSieveScriptName !== NULL) {
203             Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_testSieveScriptName);
204             try {
205                 Felamimail_Controller_Sieve::getInstance()->deleteScript($this->_account->getId());
206             } catch (Zend_Mail_Protocol_Exception $zmpe) {
207                 // do not delete script if active
208             }
209             Felamimail_Controller_Account::getInstance()->setVacationActive($this->_account, $this->_oldSieveVacationActiveState);
210             
211             if ($this->_oldSieveData !== NULL) {
212                 $this->_oldSieveData->save();
213             }
214         }
215         if ($this->_oldActiveSieveScriptName !== NULL) {
216             Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_oldActiveSieveScriptName);
217             Felamimail_Controller_Sieve::getInstance()->activateScript($this->_account->getId());
218         }
219         
220         // vfs cleanup
221         foreach ($this->_pathsToDelete as $path) {
222             $webdavRoot = new DAV\ObjectTree(new Tinebase_WebDav_Root());
223             //echo "delete $path";
224             $webdavRoot->delete($path);
225         }
226         
227         Tinebase_TransactionManager::getInstance()->rollBack();
228
229         // needed to clear cache of containers
230         Tinebase_Container::getInstance()->resetClassCache();
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
518         /* @var $contact Addressbook_Model_Contact */
519         $contact = Addressbook_Controller_Contact::getInstance()->update($contact, FALSE);
520
521         // send email
522         $messageToSend = $this->_getMessageData('unittestalias@' . $this->_mailDomain);
523         $messageToSend['note'] = 1;
524         $messageToSend['bcc']  = array(Tinebase_Core::getUser()->accountEmailAddress);
525
526         $this->_json->saveMessage($messageToSend);
527         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
528
529         // check if message is in sent folder
530         $message = $this->_searchForMessageBySubject($messageToSend['subject'], $this->_account->sent_folder);
531         $this->assertEquals($message['from_email'], $messageToSend['from_email']);
532         $this->assertTrue(isset($message['to'][0]));
533         $this->assertEquals($message['to'][0],      $messageToSend['to'][0], 'recipient not found');
534         $this->assertEquals($message['bcc'][0],     $messageToSend['bcc'][0], 'bcc recipient not found');
535         $this->assertEquals($message['subject'],    $messageToSend['subject']);
536
537         $this->_checkEmailNote($contact, $messageToSend['subject']);
538
539         // reset sclevers original email address
540         $contact->email = $originalEmail;
541         Addressbook_Controller_Contact::getInstance()->update($contact, FALSE);
542     }
543
544     /**
545      * test send message
546      */
547     public function testSendMessageInvalidMail()
548     {
549         // send email
550         $messageToSend = $this->_getMessageData('unittestalias@' . $this->_mailDomain);
551         $messageToSend['note'] = 1;
552         $messageToSend['to'] = [
553             sprintf(
554                 '%s <    %s     >',
555                 Tinebase_Core::getUser()->accountFullName,
556                 Tinebase_Core::getUser()->accountEmailAddress
557             )
558         ];
559         $messageToSend['bcc']  = array(Tinebase_Core::getUser()->accountEmailAddress);
560
561         $this->_json->saveMessage($messageToSend);
562         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
563     }
564
565     /**
566      * test send message
567      *
568      * @see 0013264: Wrong name in "from:" in sent mail
569      */
570     public function testSendMessageWithFromName()
571     {
572         // send email
573         $messageToSend = $this->_getMessageData();
574         $messageToSend['from_name'] = 'My Special Name';
575         $message = $this->_sendMessage('INBOX', array(), '', 'test', $messageToSend);
576
577         self::assertEquals($messageToSend['from_name'], $message['from_name'], print_r($message, true));
578     }
579
580     /**
581      * test mail sanitize
582      */
583     public function testSanitizeMail()
584     {
585         $expected = 'info@testest.de';
586         $obfuscatedMail = '  info@testest.de

';
587
588         $reflectionMethod = $this->getProtectedMethod(Felamimail_Model_Message::class, 'sanitizeMailAddress');
589         $result = $reflectionMethod->invokeArgs(new Felamimail_Model_Message(), [$obfuscatedMail]);
590
591         $this->assertEquals($expected, $result);
592     }
593
594     /**
595      * check email note
596      *
597      * @param Addressbook_Model_Contact $contact
598      * @param string $subject
599      */
600     protected function _checkEmailNote($contact, $subject)
601     {
602         // check if email note has been added to contact(s)
603         $contact = Addressbook_Controller_Contact::getInstance()->get($contact->getId());
604         $emailNoteType = Tinebase_Notes::getInstance()->getNoteTypeByName('email');
605         
606         // check / delete notes
607         $emailNotes = new Tinebase_Record_RecordSet('Tinebase_Model_Note');
608         foreach ($contact->notes as $note) {
609             if ($note->note_type_id == $emailNoteType->getId()) {
610                 $this->assertContains($subject, $note->note, 'did not find note subject');
611                 $this->assertEquals(Tinebase_Core::getUser()->getId(), $note->created_by);
612                 $this->assertContains('aaaaaä', $note->note);
613                 $emailNotes->addRecord($note);
614             }
615         }
616         $this->assertGreaterThan(0, $emailNotes->count(), 'no email notes found');
617         Tinebase_Notes::getInstance()->deleteNotes($emailNotes);
618     }
619
620     /**
621      * test send message to invalid recipient
622      */
623     public function testSendMessageToInvalidRecipient($invalidEmail = null, $toField = 'to', $expectedExceptionMessage = 'Recipient address rejected')
624     {
625         $this->markTestSkipped('FIXME: 0011802: Felamimail_Frontend_JsonTest::testSendMessageToInvalidRecipient fails');
626
627         $messageToSend = $this->_getMessageData($this->_account->email);
628         if ($invalidEmail === null) {
629             $invalidEmail = 'invaliduser@' . $this->_mailDomain;
630         }
631         if ($toField !== 'to') {
632             $messageToSend['to'] = array(Tinebase_Core::getUser()->accountEmailAddress);
633         }
634         $messageToSend[$toField] = array($invalidEmail);
635
636         $translation = Tinebase_Translation::getTranslation('Felamimail');
637
638         try {
639             $this->_json->saveMessage($messageToSend);
640             $this->fail('Tinebase_Exception_SystemGeneric expected');
641         } catch (Tinebase_Exception_SystemGeneric $tesg) {
642             $this->assertContains('>: ' . $translation->_($expectedExceptionMessage), $tesg->getMessage(),
643                 'exception message did not match: ' . $tesg->getMessage());
644         }
645     }
646
647     /**
648      * test send message to invalid recipients (invalid email addresses)
649      *
650      * @see 0012292: check and show invalid email addresses before sending mail
651      */
652     public function testSendMessageWithInvalidEmails()
653     {
654         $this->testSendMessageToInvalidRecipient('memyselfandi.de', 'to', 'Invalid address format');
655         $this->testSendMessageToInvalidRecipient('ich bins <mymail@ ' . $this->_mailDomain .'>', 'cc', 'Invalid address format');
656         $this->testSendMessageToInvalidRecipient('ich bins nicht <mymail\@' . $this->_mailDomain .'>', 'bcc', 'Invalid address format');
657         $this->testSendMessageToInvalidRecipient('my@mail@' . $this->_mailDomain, 'bcc', 'Invalid address format');
658     }
659
660     /**
661      * try to get a message from imap server (with complete body, attachments, etc)
662      *
663      * @see 0006300: add unique message-id header to new messages (for message-id check)
664      * @see 0012436: message-id is not valid because of double brackets
665      */
666     public function testGetMessage()
667     {
668         $message = $this->_sendMessage();
669         
670         // get complete message
671         $message = $this->_json->getMessage($message['id']);
672         
673         // check
674         $this->assertTrue(isset($message['headers']) && $message['headers']['message-id']);
675         $this->assertContains('@' . $this->_mailDomain, $message['headers']['message-id']);
676         $this->assertNotContains('<<', $message['headers']['message-id']);
677         $this->assertNotContains('>>', $message['headers']['message-id']);
678         $this->assertGreaterThan(0, preg_match('/aaaaaä/', $message['body']));
679         
680         // delete message on imap server and check if correct exception is thrown when trying to get it
681         $this->_imap->selectFolder('INBOX');
682         $this->_imap->removeMessage($message['messageuid']);
683         Tinebase_Core::getCache()->clean();
684         $this->setExpectedException('Felamimail_Exception_IMAPMessageNotFound');
685         $message = $this->_json->getMessage($message['id']);
686     }
687     
688     /**
689      * try to get a message as plain/text
690      */
691     public function testGetPlainTextMessage()
692     {
693         $accountBackend = new Felamimail_Backend_Account();
694         $message = $this->_sendMessage();
695         
696         // get complete message
697         $this->_account->display_format = Felamimail_Model_Account::DISPLAY_PLAIN;
698         $accountBackend->update($this->_account);
699         $message = $this->_json->getMessage($message['id']);
700         $this->_account->display_format = Felamimail_Model_Account::DISPLAY_HTML;
701         $accountBackend->update($this->_account);
702         
703         // check
704         $this->assertEquals("aaaaaä \n\r\n", $message['body']);
705     }
706     
707     /**
708      * try search for a message with path filter
709      */
710     public function testSearchMessageWithPathFilter()
711     {
712         $sentMessage = $this->_sendMessage();
713         $filter = array(array(
714             'field' => 'path', 'operator' => 'in', 'value' => '/' . $this->_account->getId()
715         ));
716         $result = $this->_json->searchMessages($filter, '');
717         $message = $this->_getMessageFromSearchResult($result, $sentMessage['subject']);
718         $this->assertTrue(! empty($message), 'Sent message not found with account path filter');
719
720         $inbox = $this->_getFolder('INBOX');
721         $filter = array(array(
722             'field' => 'path', 'operator' => 'in', 'value' => '/' . $this->_account->getId() . '/' . $inbox->getId()
723         ));
724         $result = $this->_json->searchMessages($filter, '');
725         $message = $this->_getMessageFromSearchResult($result, $sentMessage['subject']);
726         $this->assertTrue(! empty($message), 'Sent message not found with path filter');
727         foreach ($result['results'] as $mail) {
728             $this->assertEquals($inbox->getId(), $mail['folder_id'], 'message is in wrong folder: ' . print_r($mail, TRUE));
729         }
730     }
731     
732     /**
733      * try search for a message with all inboxes and flags filter
734      */
735     public function testSearchMessageWithAllInboxesFilter()
736     {
737         $sentMessage = $this->_sendMessage();
738         $filter = array(
739             array('field' => 'path',  'operator' => 'in',       'value' => Felamimail_Model_MessageFilter::PATH_ALLINBOXES),
740             array('field' => 'flags', 'operator' => 'notin',    'value' => Zend_Mail_Storage::FLAG_FLAGGED),
741         );
742         $result = $this->_json->searchMessages($filter, '');
743         $this->assertGreaterThan(0, $result['totalcount']);
744         $this->assertEquals($result['totalcount'], count($result['results']));
745         
746         $message = $this->_getMessageFromSearchResult($result, $sentMessage['subject']);
747         $this->assertTrue(! empty($message), 'Sent message not found with all inboxes filter');
748     }
749     
750     /**
751      * try search for a message with three cache filters to force a foreign relation join with at least 2 tables
752      */
753     public function testSearchMessageWithThreeCacheFilter()
754     {
755         $filter = array(
756             array('field' => 'flags',   'operator' => 'in',       'value' => Zend_Mail_Storage::FLAG_ANSWERED),
757             array('field' => 'to',      'operator' => 'contains', 'value' => 'testDOESNOTEXIST'),
758             array('field' => 'subject', 'operator' => 'contains', 'value' => 'testDOESNOTEXIST'),
759         );
760         $result = $this->_json->searchMessages($filter, '');
761         $this->assertEquals(0, $result['totalcount']);
762     }
763     
764     /**
765      * try search for a message with empty path filter
766      */
767     public function testSearchMessageEmptyPath()
768     {
769         $sentMessage = $this->_sendMessage();
770         
771         $filter = array(
772             array('field' => 'path',  'operator' => 'equals',   'value' => ''),
773         );
774         $result = $this->_json->searchMessages($filter, '');
775         
776         $this->assertEquals(0, $result['totalcount']);
777         $accountFilterFound = FALSE;
778         
779         foreach ($result['filter'] as $filter) {
780             if ($filter['field'] === 'account_id' && empty($filter['value'])) {
781                 $accountFilterFound = TRUE;
782                 break;
783             }
784         }
785         $this->assertTrue($accountFilterFound);
786     }
787     
788     /**
789      * test flags (add + clear + deleted)
790      */
791     public function testAddAndClearFlags()
792     {
793         $message = $this->_sendMessage();
794         $inboxBefore = $this->_getFolder('INBOX');
795         
796         $this->_json->addFlags($message['id'], Zend_Mail_Storage::FLAG_SEEN);
797         
798         // check if unread count got decreased
799         $inboxAfter = $this->_getFolder('INBOX');
800         $this->assertTrue($inboxBefore->cache_unreadcount - 1 == $inboxAfter->cache_unreadcount, 'wrong cache unreadcount');
801         
802         $message = $this->_json->getMessage($message['id']);
803         $this->assertTrue(in_array(Zend_Mail_Storage::FLAG_SEEN, $message['flags']), 'seen flag not set');
804         
805         // try with a filter
806         $filter = array(
807             array('field' => 'id', 'operator' => 'in', array($message['id']))
808         );
809         $this->_json->clearFlags($filter, Zend_Mail_Storage::FLAG_SEEN);
810         
811         $message = $this->_json->getMessage($message['id']);
812         $this->assertFalse(in_array(Zend_Mail_Storage::FLAG_SEEN, $message['flags']), 'seen flag should not be set');
813
814         $this->setExpectedException('Tinebase_Exception_NotFound');
815         $this->_json->addFlags(array($message['id']), Zend_Mail_Storage::FLAG_DELETED);
816         $this->_json->getMessage($message['id']);
817     }
818     
819     /**
820      * testMarkFolderRead
821      *
822      * @see 0009812: mark folder as read does not work with pgsql
823      */
824     public function testMarkFolderRead()
825     {
826         $inboxBefore = $this->_getFolder('INBOX');
827         $filter = array(array(
828             'field' => 'folder_id', 'operator' => 'equals', 'value' => $inboxBefore->getId()
829         ), array(
830             'field' => 'flags', 'operator' => 'notin', 'value' => array(Zend_Mail_Storage::FLAG_SEEN)
831         ));
832         $this->_json->addFlags($filter, Zend_Mail_Storage::FLAG_SEEN);
833         
834         $inboxAfter = $this->_getFolder('INBOX');
835         $this->assertEquals(0, $inboxAfter->cache_unreadcount);
836     }
837     
838     /**
839      * test delete from trash
840      */
841     public function testDeleteFromTrashWithFilter()
842     {
843         $message = $this->_sendMessage();
844         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder, $this->_account->trash_folder);
845         
846         $trash = $this->_getFolder($this->_account->trash_folder);
847         $result = $this->_json->moveMessages(array(array(
848             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
849         )), $trash->getId());
850
851         $messageInTrash = $this->_searchForMessageBySubject($message['subject'], $this->_account->trash_folder);
852         
853         // delete messages in trash with filter
854         $this->_json->addFlags(array(array(
855             'field' => 'folder_id', 'operator' => 'equals', 'value' => $trash->getId()
856         ), array(
857             'field' => 'id', 'operator' => 'in', 'value' => array($messageInTrash['id'])
858         )), Zend_Mail_Storage::FLAG_DELETED);
859         
860         $this->setExpectedException('Tinebase_Exception_NotFound');
861         $this->_json->getMessage($messageInTrash['id']);
862     }
863     
864     /**
865      * move message to trash with trash folder constant (Felamimail_Model_Folder::FOLDER_TRASH)
866      */
867     public function testMoveMessagesToTrash()
868     {
869         $message = $this->_sendMessage();
870         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder, $this->_account->trash_folder);
871         
872         $result = $this->_json->moveMessages(array(array(
873             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
874         )), Felamimail_Model_Folder::FOLDER_TRASH);
875
876         $messageInTrash = $this->_searchForMessageBySubject($message['subject'], $this->_account->trash_folder);
877     }
878     
879     /**
880      * test reply mail and check some headers
881      *
882      * @see 0006106: Add References header / https://forge.tine20.org/mantisbt/view.php?id=6106
883      */
884     public function testReplyMessage()
885     {
886         $message = $this->_sendMessage();
887         
888         $replyMessage = $this->_getReply($message);
889         $this->_json->saveMessage($replyMessage);
890         
891         $result = $this->_getMessages();
892         
893         $replyMessageFound = array();
894         $originalMessage = array();
895         foreach ($result['results'] as $mail) {
896             if ($mail['subject'] == $replyMessage['subject']) {
897                 $replyMessageFound = $mail;
898             }
899             if ($mail['subject'] == $message['subject']) {
900                 $originalMessage = $mail;
901             }
902         }
903
904         $this->assertTrue(isset($replyMessageFound['id']) && isset($originalMessage['id']), 'replied message not found');
905         $replyMessageFound = $this->_json->getMessage($replyMessageFound['id']);
906         $originalMessage = $this->_json->getMessage($originalMessage['id']);
907         
908         $this->assertTrue(! empty($replyMessageFound), 'replied message not found');
909         $this->assertTrue(! empty($originalMessage), 'original message not found');
910         
911         // check headers
912         $this->assertTrue(isset($replyMessageFound['headers']['in-reply-to']));
913         $this->assertEquals($originalMessage['headers']['message-id'], $replyMessageFound['headers']['in-reply-to']);
914         $this->assertTrue(isset($replyMessageFound['headers']['references']));
915         $this->assertEquals($originalMessage['headers']['message-id'], $replyMessageFound['headers']['references']);
916         
917         // check answered flag
918         $this->assertTrue(in_array(Zend_Mail_Storage::FLAG_ANSWERED, $originalMessage['flags'], 'could not find flag'));
919     }
920     
921     /**
922      * get reply message data
923      *
924      * @param array $_original
925      * @return array
926      */
927     protected function _getReply($_original)
928     {
929         $replyMessage               = $this->_getMessageData();
930         $replyMessage['subject']    = 'Re: ' . $_original['subject'];
931         $replyMessage['original_id']= $_original['id'];
932         $replyMessage['flags']      = Zend_Mail_Storage::FLAG_ANSWERED;
933         
934         return $replyMessage;
935     }
936
937     /**
938      * test reply mail in sent folder
939      */
940     public function testReplyMessageInSentFolder()
941     {
942         $messageInSent = $this->_sendMessage($this->_account->sent_folder);
943         $replyMessage = $this->_getReply($messageInSent);
944         $returned = $this->_json->saveMessage($replyMessage);
945         
946         $result = $this->_getMessages();
947         $sentMessage = $this->_getMessageFromSearchResult($result, $replyMessage['subject']);
948         $this->assertTrue(! empty($sentMessage));
949     }
950
951     /**
952      * test reply mail with long references header
953      *
954      * @see 0006644: "At least one mail header line is too long"
955      */
956     public function testReplyMessageWithLongHeader()
957     {
958         $messageInSent = $this->_sendMessage($this->_account->sent_folder, array(
959             'references' => '<c95d8187-2c71-437e-adb8-5e1dcdbdc507@email.test.org>
960    <2601bbfa-566e-4490-a3db-aad005733d32@email.test.org>
961    <20120530154350.1854610131@ganymed.de>
962    <7e393ce1-d193-44fc-bf5f-30c61a271fe6@email.test.org>
963    <4FC8B49C.8040704@funk.de>
964    <dba2ad5c-6726-4171-8710-984847c010a1@email.test.org>
965    <20120601123551.5E98610131@ganymed.de>
966    <f1cc3195-8641-46e3-8f20-f60f3e16b107@email.test.org>
967    <20120619093658.37E4210131@ganymed.de>
968    <CA+6Rn2PX2Q3tOk2tCQfCjcaC8zYS5XZX327OoyJfUb+w87vCLQ@mail.net.com>
969    <20120619130652.03DD310131@ganymed.de>
970    <37616c6a-4c47-4b54-9ca6-56875bc9205d@email.test.org>
971    <20120620074843.42E2010131@ganymed.de>
972    <CA+6Rn2MAb2x0qeSfcaW6F=0S7LEQL442Sx2ha9RtwMs4B0esBg@mail.net.com>
973    <20120620092902.88C8C10131@ganymed.de>
974    <c95d8187-2c71-437e-adb8-5e1dcdbdc507@email.test.org>
975    <2601bbfa-566e-4490-a3db-aad005733d32@email.test.org>
976    <20120530154350.1854610131@ganymed.de>
977    <7e393ce1-d193-44fc-bf5f-30c61a271fe6@email.test.org>
978    <4FC8B49C.8040704@funk.de>
979    <dba2ad5c-6726-4171-8710-984847c010a1@email.test.org>
980    <20120601123551.5E98610131@ganymed.de>
981    <f1cc3195-8641-46e3-8f20-f60f3e16b107@email.test.org>
982    <20120619093658.37E4210131@ganymed.de>
983    <CA+6Rn2PX2Q3tOk2tCQfCjcaC8zYS5XZX327OoyJfUb+w87vCLQ@mail.net.com>
984    <20120619130652.03DD310131@ganymed.de>
985    <37616c6a-4c47-4b54-9ca6-56875bc9205d@email.test.org>
986    <20120620074843.42E2010131@ganymed.de>
987    <CA+6Rn2MAb2x0qeSfcaW6F=0S7LEQL442Sx2ha9RtwMs4B0esBg@mail.net.com>
988    <20120620092902.88C8C10131@ganymed.de>'
989         ));
990         $replyMessage = $this->_getReply($messageInSent);
991         $returned = $this->_json->saveMessage($replyMessage);
992         
993         $result = $this->_getMessages();
994         $sentMessage = $this->_getMessageFromSearchResult($result, $replyMessage['subject']);
995         $this->assertTrue(! empty($sentMessage));
996     }
997     
998     /**
999      * test move
1000      */
1001     public function testMoveMessage()
1002     {
1003         $message = $this->_sendMessage();
1004         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder, $this->_testFolderName);
1005         
1006         $inbox = $this->_getFolder('INBOX');
1007         $inboxBefore = $this->_json->updateMessageCache($inbox['id'], 30);
1008         
1009         // move
1010         $testFolder = $this->_getFolder($this->_testFolderName);
1011         $result = $this->_json->moveMessages(array(array(
1012             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
1013         )), $testFolder->getId());
1014
1015         // sleep for 2 secs because mailserver may be slower than expected
1016         sleep(2);
1017
1018         $inboxAfter = $this->_getFolder('INBOX');
1019         
1020         // check if count was decreased correctly
1021         $this->assertEquals($inboxBefore['cache_unreadcount'] - 1, $inboxAfter['cache_unreadcount']);
1022         $this->assertEquals($inboxBefore['cache_totalcount'] - 1, $inboxAfter['cache_totalcount']);
1023         
1024         $result = $this->_getMessages($this->_testFolderName);
1025         $movedMessage = array();
1026         foreach ($result['results'] as $mail) {
1027             if ($mail['subject'] == $message['subject']) {
1028                 $movedMessage = $mail;
1029             }
1030         }
1031         $this->assertTrue(! empty($movedMessage), 'moved message not found');
1032     }
1033     
1034     /**
1035      * forward message test
1036      *
1037      * @see 0007624: losing umlauts in attached filenames
1038      */
1039     public function testForwardMessageWithAttachment()
1040     {
1041         $testFolder = $this->_getFolder($this->_testFolderName);
1042         $message = fopen(dirname(__FILE__) . '/../files/multipart_related.eml', 'r');
1043         Felamimail_Controller_Message::getInstance()->appendMessage($testFolder, $message);
1044         
1045         $subject = 'Tine 2.0 bei Metaways - Verbessurngsvorschlag';
1046         $message = $this->_searchForMessageBySubject($subject, $this->_testFolderName);
1047         
1048         $fwdSubject = 'Fwd: ' . $subject;
1049         $forwardMessageData = array(
1050             'account_id'    => $this->_account->getId(),
1051             'subject'       => $fwdSubject,
1052             'to'            => array($this->_getEmailAddress()),
1053             'body'          => "aaaaaä <br>",
1054             'headers'       => array('X-Tine20TestMessage' => 'jsontest'),
1055             'original_id'   => $message['id'],
1056             'attachments'   => array(new Tinebase_Model_TempFile(array(
1057                 'type'  => Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822,
1058                 'name'  => 'Verbessurüngsvorschlag',
1059             ), TRUE)),
1060             'flags'         => Zend_Mail_Storage::FLAG_PASSED,
1061         );
1062         
1063         $this->_foldersToClear[] = 'INBOX';
1064         $this->_json->saveMessage($forwardMessageData);
1065         $forwardMessage = $this->_searchForMessageBySubject($fwdSubject);
1066         
1067         // check attachment name
1068         $forwardMessageComplete = $this->_json->getMessage($forwardMessage['id']);
1069         $this->assertEquals(1, count($forwardMessageComplete['attachments']));
1070         $this->assertEquals('Verbessurüngsvorschlag.eml', $forwardMessageComplete['attachments'][0]['filename'], 'umlaut missing from attachment filename');
1071         
1072         $forwardMessage = $this->_json->getMessage($forwardMessage['id']);
1073         $this->assertTrue((isset($forwardMessage['structure']) || array_key_exists('structure', $forwardMessage)), 'structure should be set when fetching complete message: ' . print_r($forwardMessage, TRUE));
1074         $this->assertEquals(Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822, $forwardMessage['structure']['parts'][2]['contentType']);
1075         
1076         $message = $this->_json->getMessage($message['id']);
1077         $this->assertTrue(in_array(Zend_Mail_Storage::FLAG_PASSED, $message['flags']), 'forwarded flag missing in flags: ' . print_r($message, TRUE));
1078     }
1079     
1080     /**
1081      * testSendMessageWithAttachmentWithoutExtension
1082      *
1083      * @see 0008328: email attachment without file extension is not sent properly
1084      */
1085     public function testSendMessageWithAttachmentWithoutExtension()
1086     {
1087         $subject = 'attachment test';
1088         $messageToSend = $this->_getMessageData('unittestalias@' . $this->_mailDomain, $subject);
1089         $tempfileName = 'jsontest' . Tinebase_Record_Abstract::generateUID(10);
1090         $tempFile = $this->_createTempFile($tempfileName);
1091         $messageToSend['attachments'] = array(
1092             array('tempFile' => array('id' => $tempFile->getId(), 'type' => $tempFile->type))
1093         );
1094         $this->_json->saveMessage($messageToSend);
1095         $forwardMessage = $this->_searchForMessageBySubject($subject);
1096         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
1097         
1098         $fullMessage = $this->_json->getMessage($forwardMessage['id']);
1099         $this->assertTrue(count($fullMessage['attachments']) === 1);
1100         $attachment = $fullMessage['attachments'][0];
1101         $this->assertContains($tempfileName, $attachment['filename'], 'wrong attachment filename: ' . print_r($attachment, TRUE));
1102         $this->assertEquals(16, $attachment['size'], 'wrong attachment size: ' . print_r($attachment, TRUE));
1103     }
1104     
1105     /**
1106      * save message in folder (draft) test
1107      *
1108      * @see 0007178: BCC does not save the draft message
1109      */
1110     public function testSaveMessageInFolder()
1111     {
1112         $messageToSave = $this->_getMessageData();
1113         $messageToSave['bcc'] = array('bccaddress@email.org', 'bccaddress2@email.org');
1114         
1115         $this->_getFolder($this->_account->drafts_folder);
1116         $this->_json->saveMessageInFolder($this->_account->drafts_folder, $messageToSave);
1117         $this->_foldersToClear = array($this->_account->drafts_folder);
1118         
1119         // check if message is in drafts folder and recipients are present
1120         $message = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_account->drafts_folder);
1121         self::assertEquals($messageToSave['subject'],  $message['subject']);
1122         self::assertEquals($messageToSave['to'][0],    $message['to'][0], 'recipient not found');
1123         self::assertEquals(2, count($message['bcc']), 'bcc recipient not found: ' . print_r($message, TRUE));
1124         self::assertContains('bccaddress',   $message['bcc'][0], 'bcc recipient not found');
1125     }
1126     
1127     /**
1128      * testSendReadingConfirmation
1129      *
1130      * @see 0007736: ask user before sending reading confirmation
1131      * @see 0008402: Wrong recipient with read confirmation
1132      */
1133     public function testSendReadingConfirmation()
1134     {
1135         $messageToSave = $this->_getMessageData();
1136         $messageToSave['headers']['disposition-notification-to'] = '"' . Tinebase_Core::getUser()->accountFullName . '" <' . $this->_account->email . '>';
1137         $returned = $this->_json->saveMessageInFolder($this->_testFolderName, $messageToSave);
1138         $messageWithReadingConfirmationHeader = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_testFolderName);
1139         $this->_messageIds[] = $messageWithReadingConfirmationHeader['id'];
1140         $this->_json->sendReadingConfirmation($messageWithReadingConfirmationHeader['id']);
1141         
1142         $translate = Tinebase_Translation::getTranslation('Felamimail');
1143         $subject = $translate->_('Reading Confirmation:') . ' '. $messageToSave['subject'];
1144         $message = $this->_searchForMessageBySubject($subject);
1145         $this->_messageIds[] = $message['id'];
1146         
1147         $complete = $this->_json->getMessage($message['id']);
1148         $this->assertContains($translate->_('Was read by:') . ' ' . $this->_account->from, $complete['body']);
1149     }
1150
1151     /**
1152      * save message in non-existant folder (templates) test
1153      *
1154      * @see 0008476: Drafts are not working
1155      */
1156     public function testSaveMessageInNonExistantTemplatesFolder()
1157     {
1158         $messageToSave = $this->_getMessageData();
1159         
1160         $templatesFolder = $this->_getFolder($this->_account->templates_folder, FALSE);
1161         if ($templatesFolder) {
1162             $this->_json->deleteFolder($templatesFolder['id'], $this->_account->getId());
1163         }
1164         $returned = $this->_json->saveMessageInFolder($this->_account->templates_folder, $messageToSave);
1165         $this->_foldersToClear = array($this->_account->templates_folder);
1166         
1167         // check if message is in templates folder
1168         $message = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_account->templates_folder);
1169         $this->assertEquals($messageToSave['subject'],  $message['subject']);
1170         $this->assertEquals($messageToSave['to'][0],    $message['to'][0], 'recipient not found');
1171     }
1172     
1173     /**
1174      * testSaveMessageNoteWithInvalidChar
1175      *
1176      * @see 0008644: error when sending mail with note (wrong charset)
1177      */
1178     public function testSaveMessageNoteWithInvalidChar()
1179     {
1180         $subject = Tinebase_Core::filterInputForDatabase("\xF0\x9F\x98\x8A\xC2"); // :-) emoji &nbsp;
1181         $messageData = $this->_getMessageData('', $subject);
1182         $messageData['note'] = true;
1183         $messageData['body'] .= "&nbsp;";
1184         
1185         $this->_foldersToClear[] = 'INBOX';
1186         $this->_json->saveMessage($messageData);
1187         $message = $this->_searchForMessageBySubject($subject);
1188         
1189         $contact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
1190         $this->_checkEmailNote($contact, $subject);
1191     }
1192     
1193     /**
1194      * testSaveMessageNoteWithInvalidChar
1195      *
1196      * @see 0008644: error when sending mail with note (wrong charset)
1197      */
1198     public function testSaveMessageWithInvalidChar()
1199     {
1200         $subject = "\xF0\x9F\x98\x8A"; // :-) emoji
1201         $messageData = $this->_getMessageData('', $subject);
1202         $this->_foldersToClear[] = 'INBOX';
1203         $this->_json->saveMessage($messageData);
1204         $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
1205     }
1206
1207     /**
1208      * @see 0012160: save emails in filemanager
1209      *
1210      * @param string  $appName
1211      */
1212     public function testFileMessages($appName = 'Filemanager')
1213     {
1214         $user = Tinebase_Core::getUser();
1215         $personalFilemanagerContainer = $this->_getPersonalContainerNode($appName, $user);
1216         $message = $this->_sendMessage(
1217             'INBOX',
1218             /* $addtionalHeaders */ array(),
1219             /* $_emailFrom */ '',
1220             /*$_subject */ 'test\test' // is converted to 'test_test'
1221         );
1222         $path = '/' . Tinebase_Model_Container::TYPE_PERSONAL
1223             . '/' . $user->accountLoginName
1224             . '/' . $personalFilemanagerContainer->name;
1225         $filter = array(array(
1226             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
1227         ));
1228         $result = $this->_json->fileMessages($filter, $appName, $path);
1229         $this->assertTrue(isset($result['totalcount']));
1230         $this->assertEquals(1, $result['totalcount'], 'message should be filed in ' . $appName . ': ' . print_r($result, true));
1231
1232         // check if message exists in $appName
1233         $filter = new Tinebase_Model_Tree_Node_Filter(array(array(
1234             'field'    => 'path',
1235             'operator' => 'equals',
1236             'value'    => $path
1237         ), array(
1238             'field'    => 'name',
1239             'operator' => 'contains',
1240             'value'    => 'test_test'
1241         )));
1242         $nodeController = Tinebase_Core::getApplicationInstance($appName . '_Model_Node');
1243         $emlNode = $nodeController->search($filter)->getFirstRecord();
1244         $this->assertTrue($emlNode !== null, 'could not find eml file node');
1245         $this->assertEquals(Tinebase_Model_Tree_FileObject::TYPE_FILE, $emlNode->type);
1246         $this->assertEquals('message/rfc822', $emlNode->contenttype);
1247         $this->assertTrue(preg_match('/[a-f0-9]{10}/', $emlNode->name) == 1, 'no message id hash in node name: ' . print_r($emlNode->toArray(), true));
1248
1249         $nodeWithDescription = $nodeController->get($emlNode['id']);
1250         $this->assertTrue(isset($nodeWithDescription->description), 'description missing from node: ' . print_r($nodeWithDescription->toArray(), true));
1251         $this->assertContains($message['received'], $nodeWithDescription->description);
1252         $this->assertContains('aaaaaä', $nodeWithDescription->description);
1253     }
1254
1255     protected function _getPersonalContainerNode($_appName, $_user = null)
1256     {
1257         $user = ($_user) ? $_user : Tinebase_Core::getUser();
1258         return Tinebase_FileSystem::getInstance()->getPersonalContainer(
1259             $user,
1260             $_appName,
1261             $user
1262         )->getFirstRecord();
1263     }
1264
1265     /**
1266      * @see 0012162: create new MailFiler application
1267      */
1268     public function testFileMessagesInMailFiler()
1269     {
1270         $this->testFileMessages('MailFiler');
1271         $personalFilemanagerContainer = $this->_getPersonalContainerNode('MailFiler');
1272         $path = '/' . Tinebase_Model_Container::TYPE_PERSONAL
1273             . '/' . Tinebase_Core::getUser()->accountLoginName
1274             . '/' . $personalFilemanagerContainer->name;
1275         $filter = array(array(
1276             'field'    => 'path',
1277             'operator' => 'equals',
1278             'value'    => $path
1279         ), array(
1280             'field'    => 'subject',
1281             'operator' => 'contains',
1282             'value'    => 'test'
1283         ));
1284         $mailFilerJson = new MailFiler_Frontend_Json();
1285         $emlNodes = $mailFilerJson->searchNodes($filter, array());
1286         $this->assertGreaterThan(0, $emlNodes['totalcount'], 'could not find eml file node with subject filter');
1287         $emlNode = $emlNodes['results'][0];
1288
1289         // check email fields
1290         $this->assertTrue(isset($emlNode['message']), 'message not found in node array: ' . print_r($emlNodes['results'], true));
1291         $this->assertEquals(array(Tinebase_Core::getUser()->accountEmailAddress), $emlNode['message']['to'], print_r($emlNode['message'], true));
1292         $this->assertTrue(isset($emlNode['message']['structure']) && is_array($emlNode['message']['structure']), 'structure not found or not an array: ' . print_r($emlNode['message'], true));
1293         $this->assertTrue(isset($emlNode['message']['body']) && is_string($emlNode['message']['body']), 'body not found or not a string: ' . print_r($emlNode['message'], true));
1294         $this->assertContains('aaaaaä', $emlNode['message']['body'], print_r($emlNode['message'], true));
1295     }
1296
1297     /**
1298      * @see 0012162: create new MailFiler application
1299      */
1300     public function testFileMessagesInMailFilerWithAttachment()
1301     {
1302         $emlNode = $this->_fileMessageInMailFiler();
1303         $this->assertTrue(isset($emlNode['message']['attachments']), 'attachments not found in message node: ' . print_r($emlNode, true));
1304         $this->assertEquals(1, count($emlNode['message']['attachments']), 'attachment not found in message node: ' . print_r($emlNode, true));
1305         $this->assertEquals('moz-screenshot-83.png', $emlNode['message']['attachments'][0]['filename'], print_r($emlNode['message']['attachments'], true));
1306     }
1307
1308     /**
1309      * @param string $messageFile
1310      * @return array
1311      */
1312     protected function _fileMessageInMailFiler($messageFile = 'multipart_related.eml', $subject = 'Tine 2.0 bei Metaways - Verbessurngsvorschlag')
1313     {
1314         $appName = 'MailFiler';
1315         $personalFilemanagerContainer = $this->_getPersonalContainerNode($appName);
1316         $testFolder = $this->_getFolder($this->_testFolderName);
1317         $message = fopen(dirname(__FILE__) . '/../files/' . $messageFile, 'r');
1318         Felamimail_Controller_Message::getInstance()->appendMessage($testFolder, $message);
1319
1320         $message = $this->_searchForMessageBySubject($subject, $this->_testFolderName);
1321         $path = '/' . Tinebase_Model_Container::TYPE_PERSONAL
1322             . '/' . Tinebase_Core::getUser()->accountLoginName
1323             . '/' . $personalFilemanagerContainer->name;
1324         $filter = array(array(
1325             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
1326         ));
1327         $this->_json->fileMessages($filter, $appName, $path);
1328         $filter = array(array(
1329             'field'    => 'path',
1330             'operator' => 'equals',
1331             'value'    => $path
1332         ), array(
1333             'field'    => 'subject',
1334             'operator' => 'equals',
1335             'value'    => $message['subject']
1336         ));
1337         $mailFilerJson = new MailFiler_Frontend_Json();
1338         $emlNodes = $mailFilerJson->searchNodes($filter, array());
1339         $this->assertGreaterThan(0, $emlNodes['totalcount'], 'could not find eml file node with subject filter');
1340         $emlNode = $emlNodes['results'][0];
1341
1342         return $emlNode;
1343     }
1344
1345     /**
1346      * @see 0012162: create new MailFiler application
1347      */
1348     public function testFileMessagesInMailFilerWithSingleBodyPart()
1349     {
1350         $emlNode = $this->_fileMessageInMailFiler('tine20_alarm_notifictation.eml', 'Alarm for event "ss/ss" at Oct 12, 2016 4:00:00 PM');
1351         $this->assertContains('Event details', $emlNode['message']['body'], print_r($emlNode['message'], true));
1352         $this->assertContains('"ss/ss"', $emlNode['message']['subject'], print_r($emlNode['message'], true));
1353         $this->assertContains('"ss_ss"', $emlNode['name'], print_r($emlNode, true));
1354     }
1355
1356     /**
1357      * @see 0012162: create new MailFiler application
1358      */
1359     public function testFileMessageWithDelete()
1360     {
1361         $emlNode = $this->_fileMessageInMailFiler();
1362         $mailFilerJson = new MailFiler_Frontend_Json();
1363         $result = $mailFilerJson->deleteNodes(array($emlNode['path']));
1364         self::assertEquals('success', $result['status']);
1365     }
1366
1367     /**
1368      * @see 0012162: create new MailFiler application
1369      */
1370     public function testFileMessageWithMultipartAttachment()
1371     {
1372         $emlNode = $this->_fileMessageInMailFiler('multipart_attachments.eml', 'Testmail mit Anhang');
1373         $this->assertTrue(isset($emlNode['message']['attachments']), 'attachments not found in message node: ' . print_r($emlNode, true));
1374         $this->assertEquals(5, count($emlNode['message']['attachments']), 'attachment not found in message node: ' . print_r($emlNode, true));
1375         $this->assertEquals('TS Lagerstände.jpg', $emlNode['message']['attachments'][0]['filename'], print_r($emlNode['message']['attachments'], true));
1376         $this->assertContains('Siehe Dateien anbei', $emlNode['message']['body'], print_r($emlNode['message'], true));
1377     }
1378
1379     /**
1380      * testMessageWithInvalidICS
1381      *
1382      * @see 0008786: broken ics causes js error when showing details
1383      */
1384     public function testMessageWithInvalidICS()
1385     {
1386         $inbox = $this->_getFolder('INBOX');
1387         $mailAsString = file_get_contents(dirname(__FILE__) . '/../files/invalidimip.eml');
1388         Felamimail_Controller_Message::getInstance()->appendMessage($inbox, $mailAsString);
1389         
1390         $this->_foldersToClear = array('INBOX');
1391         $message = $this->_searchForMessageBySubject('test invalid imip');
1392         
1393         $fullMessage = $this->_json->getMessage($message['id']);
1394         $this->assertTrue(empty($fullMessage->preparedParts));
1395     }
1396
1397     /**
1398      * testSendMailveopeAPIMessage
1399      *
1400      * - envolpe amored message into PGP MIME structure
1401      */
1402     public function testSendMailveopeAPIMessage()
1403     {
1404         $subject = 'testSendMailveopeAPIMessage';
1405         $messageData = $this->_getMessageData('', $subject);
1406         $messageData['body'] = '-----BEGIN PGP MESSAGE-----
1407 Version: Mailvelope v1.3.3
1408 Comment: https://www.mailvelope.com
1409
1410 wcFMA/0LJF28pDbGAQ//YgtsmEZN+pgIJiBDb7iYwPEOchDRIEjGOx543KF6
1411 5YigW9p39pfcJgvGfT8x9cUIrYGxyw5idPSOEftYXyjjGaOYGaKpRSR4hI83
1412 OcJSlEHKq72xhg04mNpCjjJ8dLBstPcQ7tDtsA8Nfb4PwkUYB9IhIBnARg+n
1413 NvrN8mSA2UnY9ElFCvf30sar8EuM5swAjbk64C8TIypMy/Bg4T93zRdxwik6
1414 7BCcbOpm/2PTsiVYBOTcU4+XdG5eyTENXH58M6UTxTD4/g7Qi5PjN+PxyXqf
1415 v2Y1k9F49Y1egf2QJ2r4PX0EWS8SaynSHiIoBsp1xb07nLwZwCdMPG1QNPpF
1416 l2FqlS4dEuQTdkv0deMvd7gtiNynRTAVcJc1ZC6RuWJ+EH2jA49XWkn14eRC
1417 e5jMtPPudkhubnN9Je5lwatGKbJGyuXh+IaM0E0WQMZ3dm8+ST1l4WpVuGbw
1418 KozLUiTRJP9UoxWOcwpQOnzcSlc4rHmWdtF0y3usM9u9GPREqpNUWkEyEEuv
1419 XdZE7rKKj22dJHLCXxAQEh3m29Y2WVaq50YbtEZ+SwwbrHhxP4+FJEru+byh
1420 fiZ47sVW2KvYGJPvbFoSZHiSvMecxDg8BVwe+naZXww/Rwa/TlaX4bYyzpUG
1421 KeJUAzWEfFpJ0+yAvMGQEC7psIJ9NCx149C4ujiQmajSwhUB3XANcmCGB0wm
1422 JjcqC4AHvc7/t4MrQZm0F/W+nrMmNqbZk+gylVrPs9rFEqu7wbjwTmsFA3sS
1423 LkenvQIxBali6uzCR+nd09REqcYirG9cLti39DW048lhhG/ml+gAxxNEaSpG
1424 NbIoV/3w8n7sAIM1fjuHne8bX0gWG43TTjU8MwSMryG7tCOG5u+Cebh6TAoY
1425 NzbX2dpDhOYq5zXdCgKU4P3eh0csSs4UrqFT3TdAxIGrQJ7KrXvB6+N8gRZo
1426 FcUaR+zrRPJjPUZfi46ecP5SG/tM5ea1hqvkwEnEpqjLmCUxqB+rfxx46USX
1427 hMZd2ukUv6kEKv3EUDsRYu1SlDLhDLhWNx8RJae5XkMR+eUUMyNNVwbeMQbB
1428 VAcMcaPITTk84sH7XElr9eF6sCUN4V79OSBRPGY/aNGrcwcoDSD4Hwu+Lw9w
1429 Q+1n8EQ66gAkbJzCNd5GaYMZR9echkBaD/rdWDS3ktcrMehra+h44MTQONV9
1430 8W+RI+IT5jaIXtB4jePmGjsJjbC9aEhTRBRkUnPA7phgknc52dD74AY/6lzK
1431 yd4uZ6S3vhurJW0Vt4iBWJzhFNiSODh5PzteeNzCVAkGMsQvy1IHk0d3uzcE
1432 0tEuSh8fZOFGB4fvMx9Mk8oAU92wfj4J7AVpSo5oRdxMqAXfaYKqfr2Gn++q
1433 E5LClhVIBbFXclCoe0RYNz4wtxjeeYbP40Bq5g0JvPutD/dBMp8hz8Qt+yyG
1434 d8X4/KmQIXyFZ8aP17GMckE5GVVvY9y89eWnWuTUJdwM540hB/EJNeHHTE5y
1435 N2FSLGcmNkvE+3H7BczQ2ZI1SZDhof+umbUst0qoQW+hHmY3CSma48yGAVox
1436 52u2t7hosHCfpf631Ve/6fcICo8vJ2Qfufu2BGIMlSfx4WzUuaMQBynuxFSa
1437 IbVx8ZTO7dJRKrg72aFmWTf0uNla7vicAhpiLWobyNYcZbIjrAGDfg==
1438 =BaAn
1439 -----END PGP MESSAGE-----';
1440
1441         $this->_foldersToClear[] = 'INBOX';
1442         $this->_json->saveMessage($messageData);
1443
1444         $message = $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
1445         $fullMessage = $this->_json->getMessage($message['id']);
1446
1447         $this->assertContains('multipart/encrypted', $fullMessage['headers']['content-type']);
1448         $this->assertContains('protocol="application/pgp-encrypted"', $fullMessage['headers']['content-type']);
1449         $this->assertCount(2, $fullMessage['structure']['parts']);
1450         $this->assertEquals('application/pgp-encrypted', $fullMessage['structure']['parts'][1]['contentType']);
1451         $this->assertEquals('application/octet-stream', $fullMessage['structure']['parts'][2]['contentType']);
1452
1453         return $fullMessage;
1454     }
1455
1456     /**
1457      * testMessagePGPMime
1458      *
1459      * - prepare amored part of PGP MIME structure
1460      */
1461     public function testMessagePGPMime()
1462     {
1463         $fullMessage = $this->testSendMailveopeAPIMessage();
1464
1465         $this->assertEquals('application/pgp-encrypted', $fullMessage['preparedParts'][0]['contentType']);
1466         $this->assertContains('-----BEGIN PGP MESSAGE-----', $fullMessage['preparedParts'][0]['preparedData']);
1467     }
1468
1469     public function testMessagePGPInline()
1470     {
1471         $inbox = $this->_getFolder('INBOX');
1472         $mailAsString = file_get_contents(dirname(__FILE__) . '/../files/multipart_alternative_pgp_inline.eml');
1473         Felamimail_Controller_Message::getInstance()->appendMessage($inbox, $mailAsString);
1474
1475         $this->_foldersToClear = array('INBOX');
1476         $message = $this->_searchForMessageBySubject('Re: mailvelope und tine20');
1477
1478         $fullMessage = $this->_json->getMessage($message['id']);
1479         $this->assertFalse(empty($fullMessage['preparedParts']));
1480     }
1481
1482     /*********************** sieve tests ****************************/
1483     
1484     /**
1485      * set and get vacation sieve script
1486      *
1487      * @see 0007768: Sieve - Vacation notify frequency not being set (Cyrus)
1488      */
1489     public function testGetSetVacation()
1490     {
1491         $vacationData = $this->_getVacationData();
1492         $this->_sieveTestHelper($vacationData);
1493         
1494         // check if script was activated
1495         $activeScriptName = Felamimail_Controller_Sieve::getInstance()->getActiveScriptName($this->_account->getId());
1496         $this->assertEquals($this->_testSieveScriptName, $activeScriptName);
1497         $updatedAccount = Felamimail_Controller_Account::getInstance()->get($this->_account->getId());
1498         $this->assertTrue((bool) $updatedAccount->sieve_vacation_active);
1499         
1500         $result = $this->_json->getVacation($this->_account->getId());
1501
1502         $this->assertEquals($this->_account->email, $result['addresses'][0]);
1503         
1504         $sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1505         if (preg_match('/dbmail/i', $sieveBackend->getImplementation())) {
1506             $translate = Tinebase_Translation::getTranslation('Felamimail');
1507             $vacationData['subject'] = sprintf($translate->_('Out of Office reply from %1$s'), Tinebase_Core::getUser()->accountFullName);
1508         }
1509         
1510         foreach (array('reason', 'enabled', 'subject', 'from', 'days') as $field) {
1511             $this->assertEquals($vacationData[$field], $result[$field], 'vacation data mismatch: ' . $field);
1512         }
1513     }
1514     
1515     /**
1516      * get vacation data
1517      *
1518      * @return array
1519      */
1520     protected function _getVacationData()
1521     {
1522         return array(
1523             'id'                    => $this->_account->getId(),
1524             'subject'               => 'unittest vacation subject',
1525             'from'                  => $this->_account->from . ' <' . $this->_account->email . '>',
1526             'days'                  => 3,
1527             'enabled'               => TRUE,
1528             'reason'                => 'unittest vacation message<br /><br />signature',
1529             'mime'                  => NULL,
1530         );
1531     }
1532     
1533     /**
1534      * test mime vacation sieve script
1535      */
1536     public function testMimeVacation()
1537     {
1538         $vacationData = $this->_getVacationData();
1539         $vacationData['reason'] = "\n<html><body><h1>unittest vacation&nbsp;message</h1></body></html>";
1540         
1541         $_sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1542         if (! in_array('mime', $_sieveBackend->capability())) {
1543             $vacationData['mime'] = 'text/html';
1544         }
1545         
1546         $this->_sieveTestHelper($vacationData, TRUE);
1547     }
1548     
1549     /**
1550      * test get/set of rules sieve script
1551      */
1552     public function testGetSetRules()
1553     {
1554         $ruleData = $this->_getRuleData();
1555         
1556         $this->_sieveTestHelper($ruleData);
1557         
1558         // check getRules
1559         $result = $this->_json->getRules($this->_account->getId());
1560         $this->assertEquals($result['totalcount'], count($ruleData));
1561         
1562         // check by sending mail
1563         $messageData = $this->_getMessageData('', 'viagra');
1564         $returned = $this->_json->saveMessage($messageData);
1565         $this->_foldersToClear = array('INBOX', $this->_testFolderName);
1566         // check if message is in test folder
1567         $message = $this->_searchForMessageBySubject($messageData['subject'], $this->_testFolderName);
1568     }
1569     
1570     /**
1571      * testRemoveRules
1572      *
1573      * @see 0006490: can not delete single filter rule
1574      */
1575     public function testRemoveRules()
1576     {
1577         $this->testGetSetRules();
1578         $this->_json->saveRules($this->_account->getId(), array());
1579         
1580         $result = $this->_json->getRules($this->_account->getId());
1581         $this->assertEquals(0, $result['totalcount'], 'found rules: ' . print_r($result, TRUE));
1582     }
1583     
1584     /**
1585      * get sieve rule data
1586      *
1587      * @return array
1588      */
1589     protected function _getRuleData()
1590     {
1591         return array(array(
1592             'id'            => 1,
1593             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1594             'action_argument' => $this->_testFolderName,
1595             'conjunction'  => 'allof',
1596             'conditions'    => array(array(
1597                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1598                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1599                 'header'        => 'From',
1600                 'key'           => '"abcd" <info@example.org>',
1601             )),
1602             'enabled'       => 1,
1603         ), array(
1604             'id'            => 2,
1605             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1606             'action_argument' => $this->_testFolderName,
1607             'conjunction'  => 'allof',
1608             'conditions'    => array(array(
1609                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1610                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1611                 'header'        => 'From',
1612                 'key'           => 'info@example.org',
1613             )),
1614             'enabled'       => 0,
1615         ), array(
1616             'id'            => 3,
1617             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1618             'action_argument' => $this->_testFolderName,
1619             'conjunction'  => 'allof',
1620             'conditions'    => array(array(
1621                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_HEADER,
1622                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_REGEX,
1623                 'header'        => 'subject',
1624                 'key'           => '[vV]iagra|cyalis',
1625             )),
1626             'enabled'       => 1,
1627         ));
1628     }
1629     
1630     /**
1631      * test to set a forward rule to this accounts email address
1632      * -> should throw exception to prevent mail cycling
1633      */
1634     public function testSetForwardRuleToSelf()
1635     {
1636         $ruleData = array(array(
1637             'id'            => 1,
1638             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1639             'action_argument' => $this->_account->email,
1640             'conjunction'     => 'allof',
1641             'conditions'    => array(array(
1642                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1643                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1644                 'header'        => 'From',
1645                 'key'           => 'info@example.org',
1646             )),
1647             'enabled'       => 1,
1648         ));
1649         
1650         try {
1651             $this->_sieveTestHelper($ruleData);
1652             $this->assertTrue(FALSE, 'it is not allowed to set own email address for redirect!');
1653         } catch (Felamimail_Exception_Sieve $e) {
1654             $this->assertTrue(TRUE);
1655         }
1656
1657         // this should work
1658         $ruleData[0]['enabled'] = 0;
1659         $this->_sieveTestHelper($ruleData);
1660     }
1661
1662     /**
1663      * @see 0006222: Keep a copy from mails forwarded to another emailaddress
1664      */
1665     public function testSetForwardRuleWithCopy()
1666     {
1667         $ruleData = array(array(
1668             'id'            => 1,
1669             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1670             'action_argument' => array(
1671                 'emails' => 'someaccount@example.org',
1672                 'copy'   => 1,
1673             ),
1674             'conjunction'     => 'allof',
1675             'conditions'    => array(array(
1676                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1677                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1678                 'header'        => 'From',
1679                 'key'           => 'info@example.org',
1680             )),
1681             'enabled'       => 1,
1682         ));
1683
1684         $this->_sieveTestHelper($ruleData);
1685     }
1686
1687     /**
1688      * @see 0006222: Keep a copy from mails forwarded to another emailaddress
1689      */
1690     public function testSetForwardRuleWithoutCopy()
1691     {
1692         $ruleData = array(array(
1693             'id'            => 1,
1694             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1695             'action_argument' => array(
1696                 'emails' => 'someaccount@example.org',
1697                 'copy'   => 0,
1698             ),
1699             'conjunction'     => 'allof',
1700             'conditions'    => array(array(
1701                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1702                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1703                 'header'        => 'From',
1704                 'key'           => 'info@example.org',
1705             )),
1706             'enabled'       => 1,
1707         ));
1708
1709         $this->_sieveTestHelper($ruleData);
1710     }
1711
1712     /**
1713      * testGetVacationTemplates
1714      *
1715      * @return array
1716      */
1717     public function testGetVacationTemplates()
1718     {
1719         $this->_addVacationTemplateFile();
1720         $result = $this->_json->getVacationMessageTemplates();
1721         
1722         $this->assertTrue($result['totalcount'] > 0, 'no templates found');
1723         $found = FALSE;
1724         foreach ($result['results'] as $template) {
1725             if ($template['name'] === $this->_sieveVacationTemplateFile) {
1726                 $found = TRUE;
1727                 break;
1728             }
1729         }
1730         
1731         $this->assertTrue($found, 'wrong templates: ' . print_r($result['results'], TRUE));
1732         
1733         return $template;
1734     }
1735     
1736     /**
1737      * add vacation template file to vfs
1738      */
1739     protected function _addVacationTemplateFile()
1740     {
1741         $webdavRoot = new DAV\ObjectTree(new Tinebase_WebDav_Root());
1742         $path = '/webdav/Felamimail/shared/Vacation Templates';
1743         $node = $webdavRoot->getNodeForPath($path);
1744         $this->_pathsToDelete[] = $path . '/' . $this->_sieveVacationTemplateFile;
1745         $node->createFile($this->_sieveVacationTemplateFile, fopen(dirname(__FILE__) . '/../files/' . $this->_sieveVacationTemplateFile, 'r'));
1746     }
1747     
1748     /**
1749      * testGetVacationMessage
1750      */
1751     public function testGetVacationMessage()
1752     {
1753         $result = $this->_getVacationMessageWithTemplate();
1754         $sclever = Tinebase_User::getInstance()->getFullUserByLoginName('sclever');
1755         $pwulf = Tinebase_User::getInstance()->getFullUserByLoginName('pwulf');
1756         $this->assertEquals("Ich bin vom 18.04.2012 bis zum 20.04.2012 im Urlaub. Bitte kontaktieren Sie<br /> Paul Wulf (" .
1757             $pwulf->accountEmailAddress . ") oder Susan Clever (" .
1758             $sclever->accountEmailAddress . ").<br /><br />I am on vacation until Apr 20, 2012. Please contact Paul Wulf<br />(" .
1759             $pwulf->accountEmailAddress . ") or Susan Clever (" .
1760             $sclever->accountEmailAddress . ") instead.<br /><br />" .
1761             Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId())->n_fn, $result['message']);
1762     }
1763     
1764     /**
1765      * get vacation message with template
1766      *
1767      * @return array
1768      */
1769     protected function _getVacationMessageWithTemplate()
1770     {
1771         $template = $this->testGetVacationTemplates();
1772         $sclever = Tinebase_User::getInstance()->getFullUserByLoginName('sclever');
1773         $result = $this->_json->getVacationMessage(array(
1774             'start_date' => '2012-04-18',
1775             'end_date'   => '2012-04-20',
1776             'contact_ids' => array(
1777                 Tinebase_User::getInstance()->getFullUserByLoginName('pwulf')->contact_id,
1778                 $sclever->contact_id,
1779             ),
1780             'template_id' => $template['id'],
1781             'signature' => $this->_account->signature
1782         ));
1783         
1784         return $result;
1785     }
1786     
1787     /**
1788      * testGetVacationWithSignature
1789      *
1790      * @see 0006866: check signature linebreaks in vacation message from template
1791      */
1792     public function testGetVacationWithSignature()
1793     {
1794         $this->_sieveVacationTemplateFile = 'vacation_template_sig.tpl';
1795         
1796         // set signature with <br> + linebreaks
1797         $this->_account->signature = "llalala<br>\nxyz<br>\nblubb<br>";
1798         
1799         $result = $this->_getVacationMessageWithTemplate();
1800         $this->assertContains('-- <br />llalala<br />xyz<br />blubb<br />', $result['message'], 'wrong linebreaks or missing signature');
1801     }
1802     
1803     /**
1804     * testSetVacationWithStartAndEndDate
1805     *
1806     * @see 0006266: automatic deactivation of vacation message
1807     */
1808     public function testSetVacationWithStartAndEndDate()
1809     {
1810         $vacationData = $this->_getVacationData();
1811         $vacationData['start_date'] = '2012-04-18';
1812         $vacationData['end_date'] = '2012-04-20';
1813         $result = $this->_sieveTestHelper($vacationData);
1814         
1815         $this->assertContains($vacationData['start_date'], $result['start_date']);
1816         $this->assertContains($vacationData['end_date'], $result['end_date']);
1817     }
1818     
1819     /**
1820      * testSieveRulesOrder
1821      *
1822      * @see 0007240: order of sieve rules changes when vacation message is saved
1823      */
1824     public function testSieveRulesOrder()
1825     {
1826         $this->_setTestScriptname();
1827         
1828         // disable vacation first
1829         $this->_setDisabledVacation();
1830         
1831         $sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1832         
1833         $ruleData = $this->_getRuleData();
1834         $ruleData[0]['id'] = $ruleData[2]['id'];
1835         $ruleData[2]['id'] = 11;
1836         $resultSet = $this->_json->saveRules($this->_account->getId(), $ruleData);
1837         $sieveScriptRules = $sieveBackend->getScript($this->_testSieveScriptName);
1838         
1839         $this->_setDisabledVacation();
1840         $sieveScriptVacation = $sieveBackend->getScript($this->_testSieveScriptName);
1841         
1842         // compare sieve scripts
1843         $this->assertContains($sieveScriptRules, $sieveScriptVacation, 'rule order changed');
1844     }
1845
1846     public function testSieveEmailNotification()
1847     {
1848         $this->_setTestScriptname();
1849
1850         $this->_account->sieve_notification_email = 'test@test.de';
1851         Felamimail_Controller_Account::getInstance()->update($this->_account);
1852
1853         $script = new Felamimail_Sieve_Backend_Sql($this->_account->getId());
1854         $scriptParts = $script->getScriptParts();
1855         static::assertEquals(1, $scriptParts->count());
1856         /** @var Felamimail_Model_Sieve_ScriptPart $scriptPart */
1857         $scriptPart = $scriptParts->getFirstRecord();
1858         static::assertTrue(count(array_intersect(array('"enotify"', '"variables"', '"copy"', '"body"'),
1859             $scriptPart->xprops(Felamimail_Model_Sieve_ScriptPart::XPROPS_REQUIRES))) === 4,
1860             print_r($scriptPart->xprops(Felamimail_Model_Sieve_ScriptPart::XPROPS_REQUIRES), true));
1861         static::assertContains('test@test.de', $script->getSieve());
1862     }
1863
1864     /**
1865      * use another name for test sieve script
1866      */
1867     protected function _setTestScriptname()
1868     {
1869         $this->_oldActiveSieveScriptName = Felamimail_Controller_Sieve::getInstance()->getActiveScriptName($this->_account->getId());
1870         $this->_testSieveScriptName = 'Felamimail_Unittest';
1871         Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_testSieveScriptName);
1872     }
1873     
1874     /**
1875      * set disabled vacation message
1876      */
1877     protected function _setDisabledVacation()
1878     {
1879         $vacationData = $this->_getVacationData();
1880         $vacationData['enabled'] = FALSE;
1881         $resultSet = $this->_json->saveVacation($vacationData);
1882     }
1883     
1884     /**
1885      * get folder filter
1886      *
1887      * @return array
1888      */
1889     protected function _getFolderFilter()
1890     {
1891         return array(array(
1892             'field' => 'globalname', 'operator' => 'equals', 'value' => ''
1893         ));
1894     }
1895
1896     /**
1897      * get message filter
1898      *
1899      * @param string $_folderId
1900      * @return array
1901      */
1902     protected function _getMessageFilter($_folderId)
1903     {
1904         $result = array(array(
1905             'field' => 'folder_id', 'operator' => 'equals', 'value' => $_folderId
1906         ));
1907         
1908         return $result;
1909     }
1910     
1911     /**
1912      * get mailbox
1913      *
1914      * @param string $name
1915      * @param boolean $createFolder
1916      * @return Felamimail_Model_Folder|NULL
1917      */
1918     protected function _getFolder($name, $createFolder = TRUE)
1919     {
1920         Felamimail_Controller_Cache_Folder::getInstance()->update($this->_account->getId());
1921         try {
1922             $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $name);
1923         } catch (Tinebase_Exception_NotFound $tenf) {
1924             $folder = ($createFolder) ? Felamimail_Controller_Folder::getInstance()->create($this->_account, $name) : NULL;
1925         }
1926         
1927         return $folder;
1928     }
1929
1930     /**
1931      * get message data
1932      *
1933      * @return array
1934      */
1935     protected function _getMessageData($_emailFrom = '', $_subject = 'test')
1936     {
1937         return array(
1938             'account_id'    => $this->_account->getId(),
1939             'subject'       => $_subject,
1940             'to'            => [Tinebase_Core::getUser()->accountEmailAddress],
1941             'body'          => 'aaaaaä <br>',
1942             'headers'       => array('X-Tine20TestMessage' => 'jsontest'),
1943             'from_email'    => $_emailFrom,
1944             'content_type'  => Felamimail_Model_Message::CONTENT_TYPE_HTML,
1945         );
1946     }
1947
1948     /**
1949      * send message and return message array
1950      *
1951      * @param string $folderName
1952      * @param array $addtionalHeaders
1953      * @return array
1954      */
1955     protected function _sendMessage(
1956         $folderName = 'INBOX',
1957         $additionalHeaders = array(),
1958         $_emailFrom = '',
1959         $_subject = 'test',
1960         $_messageToSend = null)
1961     {
1962         $messageToSend = $_messageToSend ? $_messageToSend : $this->_getMessageData($_emailFrom, $_subject);
1963         $messageToSend['headers'] = array_merge($messageToSend['headers'], $additionalHeaders);
1964         $this->_json->saveMessage($messageToSend);
1965         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
1966
1967         $i = 0;
1968         while ($i < 5) {
1969             $result = $this->_getMessages($folderName);
1970             $message = $this->_getMessageFromSearchResult($result, $messageToSend['subject']);
1971             if (! empty($message)) {
1972                 break;
1973             }
1974             // sleep for 1 sec because mailserver may be slower than expected
1975             sleep(1);
1976             $i++;
1977         }
1978
1979         $this->assertTrue(! empty($message), 'Sent message not found.');
1980         
1981         return $message;
1982     }
1983     
1984     /**
1985      * returns message array from result
1986      *
1987      * @param array $_result
1988      * @param string $_subject
1989      * @return array
1990      */
1991     protected function _getMessageFromSearchResult($_result, $_subject)
1992     {
1993         $message = array();
1994         foreach ($_result['results'] as $mail) {
1995             if ($mail['subject'] == $_subject) {
1996                 $message = $mail;
1997             }
1998         }
1999         
2000         return $message;
2001     }
2002     
2003     /**
2004      * get messages from folder
2005      *
2006      * @param string $_folderName
2007      * @return array
2008      */
2009     protected function _getMessages($_folderName = 'INBOX')
2010     {
2011         $folder = $this->_getFolder($_folderName);
2012         $filter = $this->_getMessageFilter($folder->getId());
2013         // update cache
2014         $folder = Felamimail_Controller_Cache_Message::getInstance()->updateCache($folder, 10, 1);
2015         $i = 0;
2016         while ($folder->cache_status != Felamimail_Model_Folder::CACHE_STATUS_COMPLETE && $i < 10) {
2017             $folder = Felamimail_Controller_Cache_Message::getInstance()->updateCache($folder, 10);
2018             $i++;
2019         }
2020         $result = $this->_json->searchMessages($filter, '');
2021
2022         return $result;
2023     }
2024     
2025     /**
2026      * search for message defined by subject in folder
2027      *
2028      * @param string $_subject
2029      * @param string $_folderName
2030      * @return string message data
2031      */
2032     protected function _searchForMessageBySubject($_subject, $_folderName = 'INBOX')
2033     {
2034         // give server some time to send and receive messages
2035         sleep(1);
2036
2037         $result = $this->_getMessages($_folderName);
2038         
2039         $message = array();
2040         foreach ($result['results'] as $mail) {
2041             if ($mail['subject'] == $_subject) {
2042                 $message = $mail;
2043             }
2044         }
2045         $this->assertGreaterThan(0, $result['totalcount'], 'folder is empty');
2046         $this->assertTrue(! empty($message), 'Message not found');
2047         
2048         return $message;
2049     }
2050     
2051     /**
2052      * sieve test helper
2053      *
2054      * @param array $_sieveData
2055      * @return array
2056      */
2057     protected function _sieveTestHelper($_sieveData, $_isMime = FALSE)
2058     {
2059         $this->_setTestScriptname();
2060         
2061         // check which save fn to use
2062         if ((isset($_sieveData['reason']) || array_key_exists('reason', $_sieveData))) {
2063             $resultSet = $this->_json->saveVacation($_sieveData);
2064             $this->assertEquals($this->_account->email, $resultSet['addresses'][0]);
2065             
2066             $_sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
2067             
2068             if (preg_match('/dbmail/i', $_sieveBackend->getImplementation())) {
2069                 $translate = Tinebase_Translation::getTranslation('Felamimail');
2070                 $this->assertEquals(sprintf(
2071                     $translate->_('Out of Office reply from %1$s'), Tinebase_Core::getUser()->accountFullName),
2072                     $resultSet['subject']
2073                 );
2074             } else {
2075                 $this->assertEquals($_sieveData['subject'], $resultSet['subject']);
2076             }
2077             
2078             if ($_isMime) {
2079                 // TODO check why behaviour changed with php 7 (test was relaxed to hotfix this)
2080                 //$this->assertEquals(html_entity_decode('unittest vacation&nbsp;message', ENT_NOQUOTES, 'UTF-8'), $resultSet['reason']);
2081                 self::assertContains('unittest vacation', $resultSet['reason']);
2082             } else {
2083                 $this->assertEquals($_sieveData['reason'], $resultSet['reason']);
2084             }
2085             
2086         } else if ((isset($_sieveData[0]['action_type']) || array_key_exists('action_type', $_sieveData[0]))) {
2087             $resultSet = $this->_json->saveRules($this->_account->getId(), $_sieveData);
2088             $this->assertEquals($_sieveData, $resultSet);
2089         }
2090         
2091         return $resultSet;
2092     }
2093
2094     /**
2095      * search preferences by application felamimail
2096      *
2097      */
2098     public function testSearchFelamimailPreferences()
2099     {
2100         // search prefs
2101         $result = $this->_frontend->searchPreferencesForApplication('Felamimail', '');
2102         
2103         // check results
2104         $this->assertTrue(isset($result['results']));
2105         $this->assertGreaterThan(0, $result['totalcount']);
2106     }
2107     
2108     /**
2109      * testGetRegistryData
2110      *
2111      * @see 0010251: do not send unused config data to client
2112      */
2113     public function testGetRegistryData()
2114     {
2115         $regData = $this->_json->getRegistryData();
2116
2117         $this->assertFalse(isset($regData['defaults']));
2118         $supportedFlags = Felamimail_Config::getInstance()->featureEnabled(Felamimail_Config::FEATURE_TINE20_FLAG)
2119             ? 6
2120             : 5;
2121         $this->assertEquals($supportedFlags, $regData['supportedFlags']['totalcount']);
2122     }
2123
2124     /**
2125      * @see 0002284: add reply-to setting to email account
2126      */
2127     public function testReplyToSetting()
2128     {
2129         $this->_account->reply_to = 'noreply@tine20.org';
2130         $this->_json->saveAccount($this->_account->toArray());
2131
2132         $this->_foldersToClear[] = 'INBOX';
2133         $messageToSend = $this->_getMessageData();
2134         $this->_json->saveMessage($messageToSend);
2135         $message = $this->_searchForMessageBySubject($messageToSend['subject']);
2136
2137         $complete = $this->_json->getMessage($message['id']);
2138         $this->assertTrue(isset($complete['headers']['reply-to']), print_r($complete, true));
2139         $this->assertEquals('"Tine 2.0 Admin Account" <noreply@tine20.org>', $complete['headers']['reply-to']);
2140     }
2141
2142     /**
2143      * Its possible to choice the kind of attachment when adding it.
2144      *
2145      * type = tempfile: uploaded from harddisk, supposed to be a regular attachment
2146      *
2147      * @see 0012950: More attachment methods for mail
2148      */
2149     public function testAttachmentMethodAttachment()
2150     {
2151         $message = $this->_testAttachmentType('tempfile');
2152
2153         self::assertTrue(isset($message['attachments']), 'no attachment set: ' . print_r($message, true));
2154         self::assertEquals(1, count($message['attachments']), 'no attachment set: ' . print_r($message, true));
2155         self::assertEquals('foobar1.txt', $message['attachments'][0]['filename']);
2156         self::assertEquals(16, $message['attachments'][0]['size']);
2157     }
2158
2159     /**
2160      * Its possible to choice the kind of attachment when adding it.
2161      *
2162      * type = download_public: uploaded from harddisk, supposed to be a public download link
2163      *
2164      * @see 0012950: More attachment methods for mail
2165      */
2166     public function testAttachmentMethodPublicDownloadLinkUpload()
2167     {
2168         Zend_Registry::set('locale', new Zend_Locale('en'));
2169         $message = $this->_testAttachmentType('download_public');
2170
2171         self::assertTrue(isset($message['attachments']), 'attachment set: ' . print_r($message, true));
2172         self::assertEquals(0, count($message['attachments']), 'attachment set: ' . print_r($message, true));
2173         self::assertContains('/download', $message['body'], 'no download link in body: ' . print_r($message, true));
2174     }
2175
2176     /**
2177      * Its possible to choice the kind of attachment when adding it.
2178      *
2179      * type = download_public_fm: chosen from fm, supposed to be a public download link
2180      *
2181      * @see 0012950: More attachment methods for mail
2182      */
2183     public function testAttachmentMethodPublicDownloadLinkFromFilemanager()
2184     {
2185         $message = $this->_testAttachmentType('download_public_fm');
2186
2187         self::assertTrue(isset($message['attachments']), 'attachment set: ' . print_r($message, true));
2188         self::assertEquals(0, count($message['attachments']), 'attachment set: ' . print_r($message, true));
2189         self::assertContains('/download', $message['body'], 'no download link in body: ' . print_r($message, true));
2190     }
2191
2192     /**
2193      * Its possible to choice the kind of attachment when adding it.
2194      *
2195      * type = download_protected: uploaded from harddisk, supposed to be a protected download link
2196      *
2197      * @see 0012950: More attachment methods for mail
2198      */
2199     public function testAttachmentMethodProtectedDownloadLink()
2200     {
2201         Zend_Registry::set('locale', new Zend_Locale('en'));
2202         $message = $this->_testAttachmentType('download_protected');
2203
2204         self::assertTrue(isset($message['attachments']), 'attachment set: ' . print_r($message, true));
2205         self::assertEquals(0, count($message['attachments']), 'attachment set: ' . print_r($message, true));
2206         self::assertContains('/download', $message['body'], 'no download link in body: ' . print_r($message, true));
2207
2208         // download link id is at the end of message body
2209         $downloadLinkId = trim(substr($message['body'], -46, 40));
2210         $dl = Filemanager_Controller_DownloadLink::getInstance()->get($downloadLinkId);
2211         self::assertTrue(Filemanager_Controller_DownloadLink::getInstance()->validatePassword($dl, 'test'));
2212     }
2213
2214     /**
2215      * Its possible to choice the kind of attachment when adding it.
2216      *
2217      * type = filenode: chosen from fm, thats why type -> file, but the filemanager file is supposed to be used as a regular attachment
2218      *
2219      * @see 0012950: More attachment methods for mail
2220      */
2221     public function testAttachmentMethodFilemanagerNode()
2222     {
2223         $message = $this->_testAttachmentType('filenode');
2224
2225         self::assertTrue(isset($message['attachments']), 'no attachment set: ' . print_r($message, true));
2226         self::assertEquals(1, count($message['attachments']), 'no attachment set: ' . print_r($message, true));
2227         self::assertEquals('test.txt', $message['attachments'][0]['filename']);
2228         self::assertEquals(16, $message['attachments'][0]['size']);
2229     }
2230
2231     /**
2232      * @param $type
2233      * @return array
2234      *
2235      * @throws Filemanager_Exception_NodeExists
2236      * @throws Tinebase_Exception_InvalidArgument
2237      */
2238     protected function _testAttachmentType($type)
2239     {
2240         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
2241         $tempfile = $this->_createTempFile('foobar1.txt');
2242
2243         if (in_array($type, array('tempfile', 'download_public', 'download_protected'))) {
2244             // attach uploaded tempfile
2245             $attachment = array(
2246                 'tempFile' => $tempfile->toArray(),
2247                 'name' => 'foobar1.txt',
2248                 'size' => $tempfile->size,
2249                 'type' => 'text/plain',
2250                 'id' => 'eeabe57fd3712a9fe27a34df07cb44cab9e9afb3',
2251                 'attachment_type' => $type,
2252             );
2253
2254         } elseif (in_array($type, array('filenode', 'download_public_fm', 'download_protected_fm'))) {
2255             // attach existing file from filemanager
2256             $nodeController = Filemanager_Controller_Node::getInstance();
2257             $testPath = '/' . Tinebase_FileSystem::FOLDER_TYPE_PERSONAL . '/' . Tinebase_Core::getUser()->accountLoginName . '/testcontainer';
2258             $result = $nodeController->createNodes($testPath, Tinebase_Model_Tree_FileObject::TYPE_FOLDER, array(), FALSE);
2259             $personalNode = $result[0];
2260             $filepath = $personalNode->path . '/test.txt';
2261
2262             // create empty file first (like the js frontend does)
2263             $nodeController->createNodes($filepath, Tinebase_Model_Tree_FileObject::TYPE_FILE, array(), FALSE);
2264             $fmFile = $nodeController->createNodes(
2265                 $filepath,
2266                 Tinebase_Model_Tree_FileObject::TYPE_FILE,
2267                 (array)$tempfile->getId(), TRUE
2268             )->getFirstRecord();
2269
2270             $attachment = [ // chosen from fm, thats why type -> file, but the filemanager file is supposed to be used as a regular attachment
2271                 'name' => $fmFile->name,
2272                 'path' => $fmFile->path,
2273                 'size' => $fmFile->size,
2274                 'type' => $fmFile->contenttype,
2275                 'id' => $fmFile->getId(),
2276                 'attachment_type' => $type,
2277             ];
2278
2279         } else {
2280             throw new Tinebase_Exception_InvalidArgument('invalid type given');
2281         }
2282
2283         if (in_array($type, array('download_protected_fm', 'download_protected'))) {
2284             $attachment['password'] = 'test';
2285         }
2286
2287         $messageToSend = [
2288             'note' => null,
2289             'content_type' => 'text/html',
2290             'account_id' => $this->_account->id,
2291             'to' => [
2292                 $this->_account->email
2293             ],
2294             'cc' => [],
2295             'bcc' => [],
2296             'subject' => 'attachment test [' . $type . ']',
2297             'body' => 'foobar',
2298
2299             'attachments' => [ $attachment ],
2300             'from_email' => 'vagrant@example.org',
2301             'customfields' => [],
2302             'headers' => array('X-Tine20TestMessage' => 'jsontest'),
2303         ];
2304
2305         $this->_json->saveMessage($messageToSend);
2306         $message = $this->_searchForMessageBySubject($messageToSend['subject']);
2307         $complete = $this->_json->getMessage($message['id']);
2308
2309         return $complete;
2310     }
2311
2312     /**
2313      * @param string $tempfileName
2314      * @return Tinebase_Model_TempFile
2315      */
2316     protected function _createTempFile($tempfileName = 'test.txt')
2317     {
2318         $tempfilePath = Tinebase_Core::getTempDir() . DIRECTORY_SEPARATOR . $tempfileName;
2319         file_put_contents($tempfilePath, 'some content');
2320         return Tinebase_TempFile::getInstance()->createTempFile($tempfilePath, $tempfileName);
2321     }
2322 }