Merge branch '2016.11' into 2016.11-develop
[tine20] / tests / tine20 / Felamimail / Frontend / JsonTest.php
1 <?php
2
3 use Sabre\DAV;
4
5 /**
6  * Tine 2.0 - http://www.tine20.org
7  *
8  * @package     Felamimail
9  * @license     http://www.gnu.org/licenses/agpl.html
10  * @copyright   Copyright (c) 2009-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         $draftsFolder = $this->_getFolder($this->_account->drafts_folder);
1116         $returned = $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         $this->assertEquals($messageToSave['subject'],  $message['subject']);
1122         $this->assertEquals($messageToSave['to'][0],    $message['to'][0], 'recipient not found');
1123         $this->assertEquals(2, count($message['bcc']), 'bcc recipient not found: ' . print_r($message, TRUE));
1124         $this->assertEquals($messageToSave['bcc'][0],   $message['bcc'][0], '1st bcc recipient not found');
1125         $this->assertEquals($messageToSave['bcc'][1],   $message['bcc'][1], '2nd bcc recipient not found');
1126     }
1127     
1128     /**
1129      * testSendReadingConfirmation
1130      *
1131      * @see 0007736: ask user before sending reading confirmation
1132      * @see 0008402: Wrong recipient with read confirmation
1133      */
1134     public function testSendReadingConfirmation()
1135     {
1136         $messageToSave = $this->_getMessageData();
1137         $messageToSave['headers']['disposition-notification-to'] = '"' . Tinebase_Core::getUser()->accountFullName . '" <' . $this->_account->email . '>';
1138         $returned = $this->_json->saveMessageInFolder($this->_testFolderName, $messageToSave);
1139         $messageWithReadingConfirmationHeader = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_testFolderName);
1140         $this->_messageIds[] = $messageWithReadingConfirmationHeader['id'];
1141         $this->_json->sendReadingConfirmation($messageWithReadingConfirmationHeader['id']);
1142         
1143         $translate = Tinebase_Translation::getTranslation('Felamimail');
1144         $subject = $translate->_('Reading Confirmation:') . ' '. $messageToSave['subject'];
1145         $message = $this->_searchForMessageBySubject($subject);
1146         $this->_messageIds[] = $message['id'];
1147         
1148         $complete = $this->_json->getMessage($message['id']);
1149         $this->assertContains($translate->_('Was read by:') . ' ' . $this->_account->from, $complete['body']);
1150     }
1151
1152     /**
1153      * save message in non-existant folder (templates) test
1154      *
1155      * @see 0008476: Drafts are not working
1156      */
1157     public function testSaveMessageInNonExistantTemplatesFolder()
1158     {
1159         $messageToSave = $this->_getMessageData();
1160         
1161         $templatesFolder = $this->_getFolder($this->_account->templates_folder, FALSE);
1162         if ($templatesFolder) {
1163             $this->_json->deleteFolder($templatesFolder['id'], $this->_account->getId());
1164         }
1165         $returned = $this->_json->saveMessageInFolder($this->_account->templates_folder, $messageToSave);
1166         $this->_foldersToClear = array($this->_account->templates_folder);
1167         
1168         // check if message is in templates folder
1169         $message = $this->_searchForMessageBySubject($messageToSave['subject'], $this->_account->templates_folder);
1170         $this->assertEquals($messageToSave['subject'],  $message['subject']);
1171         $this->assertEquals($messageToSave['to'][0],    $message['to'][0], 'recipient not found');
1172     }
1173     
1174     /**
1175      * testSaveMessageNoteWithInvalidChar
1176      *
1177      * @see 0008644: error when sending mail with note (wrong charset)
1178      */
1179     public function testSaveMessageNoteWithInvalidChar()
1180     {
1181         $subject = Tinebase_Core::filterInputForDatabase("\xF0\x9F\x98\x8A\xC2"); // :-) emoji &nbsp;
1182         $messageData = $this->_getMessageData('', $subject);
1183         $messageData['note'] = true;
1184         $messageData['body'] .= "&nbsp;";
1185         
1186         $this->_foldersToClear[] = 'INBOX';
1187         $this->_json->saveMessage($messageData);
1188         $message = $this->_searchForMessageBySubject($subject);
1189         
1190         $contact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
1191         $this->_checkEmailNote($contact, $subject);
1192     }
1193     
1194     /**
1195      * testSaveMessageNoteWithInvalidChar
1196      *
1197      * @see 0008644: error when sending mail with note (wrong charset)
1198      */
1199     public function testSaveMessageWithInvalidChar()
1200     {
1201         $subject = "\xF0\x9F\x98\x8A"; // :-) emoji
1202         $messageData = $this->_getMessageData('', $subject);
1203         $this->_foldersToClear[] = 'INBOX';
1204         $this->_json->saveMessage($messageData);
1205         $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
1206     }
1207
1208     /**
1209      * @see 0012160: save emails in filemanager
1210      *
1211      * @param string  $appName
1212      */
1213     public function testFileMessages($appName = 'Filemanager')
1214     {
1215         $user = Tinebase_Core::getUser();
1216         $personalFilemanagerContainer = $this->_getPersonalContainerNode($appName, $user);
1217         $message = $this->_sendMessage(
1218             'INBOX',
1219             /* $addtionalHeaders */ array(),
1220             /* $_emailFrom */ '',
1221             /*$_subject */ 'test\test' // is converted to 'test_test'
1222         );
1223         $path = '/' . Tinebase_Model_Container::TYPE_PERSONAL
1224             . '/' . $user->accountLoginName
1225             . '/' . $personalFilemanagerContainer->name;
1226         $filter = array(array(
1227             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
1228         ));
1229         $result = $this->_json->fileMessages($filter, $appName, $path);
1230         $this->assertTrue(isset($result['totalcount']));
1231         $this->assertEquals(1, $result['totalcount'], 'message should be filed in ' . $appName . ': ' . print_r($result, true));
1232
1233         // check if message exists in $appName
1234         $filter = new Tinebase_Model_Tree_Node_Filter(array(array(
1235             'field'    => 'path',
1236             'operator' => 'equals',
1237             'value'    => $path
1238         ), array(
1239             'field'    => 'name',
1240             'operator' => 'contains',
1241             'value'    => 'test_test'
1242         )));
1243         $nodeController = Tinebase_Core::getApplicationInstance($appName . '_Model_Node');
1244         $emlNode = $nodeController->search($filter)->getFirstRecord();
1245         $this->assertTrue($emlNode !== null, 'could not find eml file node');
1246         $this->assertEquals(Tinebase_Model_Tree_FileObject::TYPE_FILE, $emlNode->type);
1247         $this->assertEquals('message/rfc822', $emlNode->contenttype);
1248         $this->assertTrue(preg_match('/[a-f0-9]{10}/', $emlNode->name) == 1, 'no message id hash in node name: ' . print_r($emlNode->toArray(), true));
1249
1250         $nodeWithDescription = $nodeController->get($emlNode['id']);
1251         $this->assertTrue(isset($nodeWithDescription->description), 'description missing from node: ' . print_r($nodeWithDescription->toArray(), true));
1252         $this->assertContains($message['received'], $nodeWithDescription->description);
1253         $this->assertContains('aaaaaä', $nodeWithDescription->description);
1254     }
1255
1256     protected function _getPersonalContainerNode($_appName, $_user = null)
1257     {
1258         $user = ($_user) ? $_user : Tinebase_Core::getUser();
1259         return Tinebase_FileSystem::getInstance()->getPersonalContainer(
1260             $user,
1261             $_appName,
1262             $user
1263         )->getFirstRecord();
1264     }
1265
1266     /**
1267      * @see 0012162: create new MailFiler application
1268      */
1269     public function testFileMessagesInMailFiler()
1270     {
1271         $this->testFileMessages('MailFiler');
1272         $personalFilemanagerContainer = $this->_getPersonalContainerNode('MailFiler');
1273         $path = '/' . Tinebase_Model_Container::TYPE_PERSONAL
1274             . '/' . Tinebase_Core::getUser()->accountLoginName
1275             . '/' . $personalFilemanagerContainer->name;
1276         $filter = array(array(
1277             'field'    => 'path',
1278             'operator' => 'equals',
1279             'value'    => $path
1280         ), array(
1281             'field'    => 'subject',
1282             'operator' => 'contains',
1283             'value'    => 'test'
1284         ));
1285         $mailFilerJson = new MailFiler_Frontend_Json();
1286         $emlNodes = $mailFilerJson->searchNodes($filter, array());
1287         $this->assertGreaterThan(0, $emlNodes['totalcount'], 'could not find eml file node with subject filter');
1288         $emlNode = $emlNodes['results'][0];
1289
1290         // check email fields
1291         $this->assertTrue(isset($emlNode['message']), 'message not found in node array: ' . print_r($emlNodes['results'], true));
1292         $this->assertEquals(array(Tinebase_Core::getUser()->accountEmailAddress), $emlNode['message']['to'], print_r($emlNode['message'], true));
1293         $this->assertTrue(isset($emlNode['message']['structure']) && is_array($emlNode['message']['structure']), 'structure not found or not an array: ' . print_r($emlNode['message'], true));
1294         $this->assertTrue(isset($emlNode['message']['body']) && is_string($emlNode['message']['body']), 'body not found or not a string: ' . print_r($emlNode['message'], true));
1295         $this->assertContains('aaaaaä', $emlNode['message']['body'], print_r($emlNode['message'], true));
1296     }
1297
1298     /**
1299      * @see 0012162: create new MailFiler application
1300      */
1301     public function testFileMessagesInMailFilerWithAttachment()
1302     {
1303         $emlNode = $this->_fileMessageInMailFiler();
1304         $this->assertTrue(isset($emlNode['message']['attachments']), 'attachments not found in message node: ' . print_r($emlNode, true));
1305         $this->assertEquals(1, count($emlNode['message']['attachments']), 'attachment not found in message node: ' . print_r($emlNode, true));
1306         $this->assertEquals('moz-screenshot-83.png', $emlNode['message']['attachments'][0]['filename'], print_r($emlNode['message']['attachments'], true));
1307     }
1308
1309     /**
1310      * @param string $messageFile
1311      * @return array
1312      */
1313     protected function _fileMessageInMailFiler($messageFile = 'multipart_related.eml', $subject = 'Tine 2.0 bei Metaways - Verbessurngsvorschlag')
1314     {
1315         $appName = 'MailFiler';
1316         $personalFilemanagerContainer = $this->_getPersonalContainerNode($appName);
1317         $testFolder = $this->_getFolder($this->_testFolderName);
1318         $message = fopen(dirname(__FILE__) . '/../files/' . $messageFile, 'r');
1319         Felamimail_Controller_Message::getInstance()->appendMessage($testFolder, $message);
1320
1321         $message = $this->_searchForMessageBySubject($subject, $this->_testFolderName);
1322         $path = '/' . Tinebase_Model_Container::TYPE_PERSONAL
1323             . '/' . Tinebase_Core::getUser()->accountLoginName
1324             . '/' . $personalFilemanagerContainer->name;
1325         $filter = array(array(
1326             'field' => 'id', 'operator' => 'in', 'value' => array($message['id'])
1327         ));
1328         $this->_json->fileMessages($filter, $appName, $path);
1329         $filter = array(array(
1330             'field'    => 'path',
1331             'operator' => 'equals',
1332             'value'    => $path
1333         ), array(
1334             'field'    => 'subject',
1335             'operator' => 'equals',
1336             'value'    => $message['subject']
1337         ));
1338         $mailFilerJson = new MailFiler_Frontend_Json();
1339         $emlNodes = $mailFilerJson->searchNodes($filter, array());
1340         $this->assertGreaterThan(0, $emlNodes['totalcount'], 'could not find eml file node with subject filter');
1341         $emlNode = $emlNodes['results'][0];
1342
1343         return $emlNode;
1344     }
1345
1346     /**
1347      * @see 0012162: create new MailFiler application
1348      */
1349     public function testFileMessagesInMailFilerWithSingleBodyPart()
1350     {
1351         $emlNode = $this->_fileMessageInMailFiler('tine20_alarm_notifictation.eml', 'Alarm for event "ss/ss" at Oct 12, 2016 4:00:00 PM');
1352         $this->assertContains('Event details', $emlNode['message']['body'], print_r($emlNode['message'], true));
1353         $this->assertContains('"ss/ss"', $emlNode['message']['subject'], print_r($emlNode['message'], true));
1354         $this->assertContains('"ss_ss"', $emlNode['name'], print_r($emlNode, true));
1355     }
1356
1357     /**
1358      * @see 0012162: create new MailFiler application
1359      */
1360     public function testFileMessageWithDelete()
1361     {
1362         $emlNode = $this->_fileMessageInMailFiler();
1363         $mailFilerJson = new MailFiler_Frontend_Json();
1364         $result = $mailFilerJson->deleteNodes(array($emlNode['path']));
1365         self::assertEquals('success', $result['status']);
1366     }
1367
1368     /**
1369      * @see 0012162: create new MailFiler application
1370      */
1371     public function testFileMessageWithMultipartAttachment()
1372     {
1373         $emlNode = $this->_fileMessageInMailFiler('multipart_attachments.eml', 'Testmail mit Anhang');
1374         $this->assertTrue(isset($emlNode['message']['attachments']), 'attachments not found in message node: ' . print_r($emlNode, true));
1375         $this->assertEquals(5, count($emlNode['message']['attachments']), 'attachment not found in message node: ' . print_r($emlNode, true));
1376         $this->assertEquals('TS Lagerstände.jpg', $emlNode['message']['attachments'][0]['filename'], print_r($emlNode['message']['attachments'], true));
1377         $this->assertContains('Siehe Dateien anbei', $emlNode['message']['body'], print_r($emlNode['message'], true));
1378     }
1379
1380     /**
1381      * testMessageWithInvalidICS
1382      *
1383      * @see 0008786: broken ics causes js error when showing details
1384      */
1385     public function testMessageWithInvalidICS()
1386     {
1387         $inbox = $this->_getFolder('INBOX');
1388         $mailAsString = file_get_contents(dirname(__FILE__) . '/../files/invalidimip.eml');
1389         Felamimail_Controller_Message::getInstance()->appendMessage($inbox, $mailAsString);
1390         
1391         $this->_foldersToClear = array('INBOX');
1392         $message = $this->_searchForMessageBySubject('test invalid imip');
1393         
1394         $fullMessage = $this->_json->getMessage($message['id']);
1395         $this->assertTrue(empty($fullMessage->preparedParts));
1396     }
1397
1398     /**
1399      * testSendMailveopeAPIMessage
1400      *
1401      * - envolpe amored message into PGP MIME structure
1402      */
1403     public function testSendMailveopeAPIMessage()
1404     {
1405         $subject = 'testSendMailveopeAPIMessage';
1406         $messageData = $this->_getMessageData('', $subject);
1407         $messageData['body'] = '-----BEGIN PGP MESSAGE-----
1408 Version: Mailvelope v1.3.3
1409 Comment: https://www.mailvelope.com
1410
1411 wcFMA/0LJF28pDbGAQ//YgtsmEZN+pgIJiBDb7iYwPEOchDRIEjGOx543KF6
1412 5YigW9p39pfcJgvGfT8x9cUIrYGxyw5idPSOEftYXyjjGaOYGaKpRSR4hI83
1413 OcJSlEHKq72xhg04mNpCjjJ8dLBstPcQ7tDtsA8Nfb4PwkUYB9IhIBnARg+n
1414 NvrN8mSA2UnY9ElFCvf30sar8EuM5swAjbk64C8TIypMy/Bg4T93zRdxwik6
1415 7BCcbOpm/2PTsiVYBOTcU4+XdG5eyTENXH58M6UTxTD4/g7Qi5PjN+PxyXqf
1416 v2Y1k9F49Y1egf2QJ2r4PX0EWS8SaynSHiIoBsp1xb07nLwZwCdMPG1QNPpF
1417 l2FqlS4dEuQTdkv0deMvd7gtiNynRTAVcJc1ZC6RuWJ+EH2jA49XWkn14eRC
1418 e5jMtPPudkhubnN9Je5lwatGKbJGyuXh+IaM0E0WQMZ3dm8+ST1l4WpVuGbw
1419 KozLUiTRJP9UoxWOcwpQOnzcSlc4rHmWdtF0y3usM9u9GPREqpNUWkEyEEuv
1420 XdZE7rKKj22dJHLCXxAQEh3m29Y2WVaq50YbtEZ+SwwbrHhxP4+FJEru+byh
1421 fiZ47sVW2KvYGJPvbFoSZHiSvMecxDg8BVwe+naZXww/Rwa/TlaX4bYyzpUG
1422 KeJUAzWEfFpJ0+yAvMGQEC7psIJ9NCx149C4ujiQmajSwhUB3XANcmCGB0wm
1423 JjcqC4AHvc7/t4MrQZm0F/W+nrMmNqbZk+gylVrPs9rFEqu7wbjwTmsFA3sS
1424 LkenvQIxBali6uzCR+nd09REqcYirG9cLti39DW048lhhG/ml+gAxxNEaSpG
1425 NbIoV/3w8n7sAIM1fjuHne8bX0gWG43TTjU8MwSMryG7tCOG5u+Cebh6TAoY
1426 NzbX2dpDhOYq5zXdCgKU4P3eh0csSs4UrqFT3TdAxIGrQJ7KrXvB6+N8gRZo
1427 FcUaR+zrRPJjPUZfi46ecP5SG/tM5ea1hqvkwEnEpqjLmCUxqB+rfxx46USX
1428 hMZd2ukUv6kEKv3EUDsRYu1SlDLhDLhWNx8RJae5XkMR+eUUMyNNVwbeMQbB
1429 VAcMcaPITTk84sH7XElr9eF6sCUN4V79OSBRPGY/aNGrcwcoDSD4Hwu+Lw9w
1430 Q+1n8EQ66gAkbJzCNd5GaYMZR9echkBaD/rdWDS3ktcrMehra+h44MTQONV9
1431 8W+RI+IT5jaIXtB4jePmGjsJjbC9aEhTRBRkUnPA7phgknc52dD74AY/6lzK
1432 yd4uZ6S3vhurJW0Vt4iBWJzhFNiSODh5PzteeNzCVAkGMsQvy1IHk0d3uzcE
1433 0tEuSh8fZOFGB4fvMx9Mk8oAU92wfj4J7AVpSo5oRdxMqAXfaYKqfr2Gn++q
1434 E5LClhVIBbFXclCoe0RYNz4wtxjeeYbP40Bq5g0JvPutD/dBMp8hz8Qt+yyG
1435 d8X4/KmQIXyFZ8aP17GMckE5GVVvY9y89eWnWuTUJdwM540hB/EJNeHHTE5y
1436 N2FSLGcmNkvE+3H7BczQ2ZI1SZDhof+umbUst0qoQW+hHmY3CSma48yGAVox
1437 52u2t7hosHCfpf631Ve/6fcICo8vJ2Qfufu2BGIMlSfx4WzUuaMQBynuxFSa
1438 IbVx8ZTO7dJRKrg72aFmWTf0uNla7vicAhpiLWobyNYcZbIjrAGDfg==
1439 =BaAn
1440 -----END PGP MESSAGE-----';
1441
1442         $this->_foldersToClear[] = 'INBOX';
1443         $this->_json->saveMessage($messageData);
1444
1445         $message = $this->_searchForMessageBySubject(Tinebase_Core::filterInputForDatabase($subject));
1446         $fullMessage = $this->_json->getMessage($message['id']);
1447
1448         $this->assertContains('multipart/encrypted', $fullMessage['headers']['content-type']);
1449         $this->assertContains('protocol="application/pgp-encrypted"', $fullMessage['headers']['content-type']);
1450         $this->assertCount(2, $fullMessage['structure']['parts']);
1451         $this->assertEquals('application/pgp-encrypted', $fullMessage['structure']['parts'][1]['contentType']);
1452         $this->assertEquals('application/octet-stream', $fullMessage['structure']['parts'][2]['contentType']);
1453
1454         return $fullMessage;
1455     }
1456
1457     /**
1458      * testMessagePGPMime
1459      *
1460      * - prepare amored part of PGP MIME structure
1461      */
1462     public function testMessagePGPMime()
1463     {
1464         $fullMessage = $this->testSendMailveopeAPIMessage();
1465
1466         $this->assertEquals('application/pgp-encrypted', $fullMessage['preparedParts'][0]['contentType']);
1467         $this->assertContains('-----BEGIN PGP MESSAGE-----', $fullMessage['preparedParts'][0]['preparedData']);
1468     }
1469
1470     public function testMessagePGPInline()
1471     {
1472         $inbox = $this->_getFolder('INBOX');
1473         $mailAsString = file_get_contents(dirname(__FILE__) . '/../files/multipart_alternative_pgp_inline.eml');
1474         Felamimail_Controller_Message::getInstance()->appendMessage($inbox, $mailAsString);
1475
1476         $this->_foldersToClear = array('INBOX');
1477         $message = $this->_searchForMessageBySubject('Re: mailvelope und tine20');
1478
1479         $fullMessage = $this->_json->getMessage($message['id']);
1480         $this->assertFalse(empty($fullMessage['preparedParts']));
1481     }
1482
1483     /*********************** sieve tests ****************************/
1484     
1485     /**
1486      * set and get vacation sieve script
1487      *
1488      * @see 0007768: Sieve - Vacation notify frequency not being set (Cyrus)
1489      */
1490     public function testGetSetVacation()
1491     {
1492         $vacationData = $this->_getVacationData();
1493         $this->_sieveTestHelper($vacationData);
1494         
1495         // check if script was activated
1496         $activeScriptName = Felamimail_Controller_Sieve::getInstance()->getActiveScriptName($this->_account->getId());
1497         $this->assertEquals($this->_testSieveScriptName, $activeScriptName);
1498         $updatedAccount = Felamimail_Controller_Account::getInstance()->get($this->_account->getId());
1499         $this->assertTrue((bool) $updatedAccount->sieve_vacation_active);
1500         
1501         $result = $this->_json->getVacation($this->_account->getId());
1502
1503         $this->assertEquals($this->_account->email, $result['addresses'][0]);
1504         
1505         $sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1506         if (preg_match('/dbmail/i', $sieveBackend->getImplementation())) {
1507             $translate = Tinebase_Translation::getTranslation('Felamimail');
1508             $vacationData['subject'] = sprintf($translate->_('Out of Office reply from %1$s'), Tinebase_Core::getUser()->accountFullName);
1509         }
1510         
1511         foreach (array('reason', 'enabled', 'subject', 'from', 'days') as $field) {
1512             $this->assertEquals($vacationData[$field], $result[$field], 'vacation data mismatch: ' . $field);
1513         }
1514     }
1515     
1516     /**
1517      * get vacation data
1518      *
1519      * @return array
1520      */
1521     protected function _getVacationData()
1522     {
1523         return array(
1524             'id'                    => $this->_account->getId(),
1525             'subject'               => 'unittest vacation subject',
1526             'from'                  => $this->_account->from . ' <' . $this->_account->email . '>',
1527             'days'                  => 3,
1528             'enabled'               => TRUE,
1529             'reason'                => 'unittest vacation message<br /><br />signature',
1530             'mime'                  => NULL,
1531         );
1532     }
1533     
1534     /**
1535      * test mime vacation sieve script
1536      */
1537     public function testMimeVacation()
1538     {
1539         $vacationData = $this->_getVacationData();
1540         $vacationData['reason'] = "\n<html><body><h1>unittest vacation&nbsp;message</h1></body></html>";
1541         
1542         $_sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1543         if (! in_array('mime', $_sieveBackend->capability())) {
1544             $vacationData['mime'] = 'text/html';
1545         }
1546         
1547         $this->_sieveTestHelper($vacationData, TRUE);
1548     }
1549     
1550     /**
1551      * test get/set of rules sieve script
1552      */
1553     public function testGetSetRules()
1554     {
1555         $ruleData = $this->_getRuleData();
1556         
1557         $this->_sieveTestHelper($ruleData);
1558         
1559         // check getRules
1560         $result = $this->_json->getRules($this->_account->getId());
1561         $this->assertEquals($result['totalcount'], count($ruleData));
1562         
1563         // check by sending mail
1564         $messageData = $this->_getMessageData('', 'viagra');
1565         $returned = $this->_json->saveMessage($messageData);
1566         $this->_foldersToClear = array('INBOX', $this->_testFolderName);
1567         // check if message is in test folder
1568         $message = $this->_searchForMessageBySubject($messageData['subject'], $this->_testFolderName);
1569     }
1570     
1571     /**
1572      * testRemoveRules
1573      *
1574      * @see 0006490: can not delete single filter rule
1575      */
1576     public function testRemoveRules()
1577     {
1578         $this->testGetSetRules();
1579         $this->_json->saveRules($this->_account->getId(), array());
1580         
1581         $result = $this->_json->getRules($this->_account->getId());
1582         $this->assertEquals(0, $result['totalcount'], 'found rules: ' . print_r($result, TRUE));
1583     }
1584     
1585     /**
1586      * get sieve rule data
1587      *
1588      * @return array
1589      */
1590     protected function _getRuleData()
1591     {
1592         return array(array(
1593             'id'            => 1,
1594             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1595             'action_argument' => $this->_testFolderName,
1596             'conjunction'  => 'allof',
1597             'conditions'    => array(array(
1598                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1599                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1600                 'header'        => 'From',
1601                 'key'           => '"abcd" <info@example.org>',
1602             )),
1603             'enabled'       => 1,
1604         ), array(
1605             'id'            => 2,
1606             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1607             'action_argument' => $this->_testFolderName,
1608             'conjunction'  => 'allof',
1609             'conditions'    => array(array(
1610                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1611                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1612                 'header'        => 'From',
1613                 'key'           => 'info@example.org',
1614             )),
1615             'enabled'       => 0,
1616         ), array(
1617             'id'            => 3,
1618             'action_type'   => Felamimail_Sieve_Rule_Action::FILEINTO,
1619             'action_argument' => $this->_testFolderName,
1620             'conjunction'  => 'allof',
1621             'conditions'    => array(array(
1622                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_HEADER,
1623                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_REGEX,
1624                 'header'        => 'subject',
1625                 'key'           => '[vV]iagra|cyalis',
1626             )),
1627             'enabled'       => 1,
1628         ));
1629     }
1630     
1631     /**
1632      * test to set a forward rule to this accounts email address
1633      * -> should throw exception to prevent mail cycling
1634      */
1635     public function testSetForwardRuleToSelf()
1636     {
1637         $ruleData = array(array(
1638             'id'            => 1,
1639             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1640             'action_argument' => $this->_account->email,
1641             'conjunction'     => 'allof',
1642             'conditions'    => array(array(
1643                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1644                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1645                 'header'        => 'From',
1646                 'key'           => 'info@example.org',
1647             )),
1648             'enabled'       => 1,
1649         ));
1650         
1651         try {
1652             $this->_sieveTestHelper($ruleData);
1653             $this->assertTrue(FALSE, 'it is not allowed to set own email address for redirect!');
1654         } catch (Felamimail_Exception_Sieve $e) {
1655             $this->assertTrue(TRUE);
1656         }
1657
1658         // this should work
1659         $ruleData[0]['enabled'] = 0;
1660         $this->_sieveTestHelper($ruleData);
1661     }
1662
1663     /**
1664      * @see 0006222: Keep a copy from mails forwarded to another emailaddress
1665      */
1666     public function testSetForwardRuleWithCopy()
1667     {
1668         $ruleData = array(array(
1669             'id'            => 1,
1670             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1671             'action_argument' => array(
1672                 'emails' => 'someaccount@example.org',
1673                 'copy'   => 1,
1674             ),
1675             'conjunction'     => 'allof',
1676             'conditions'    => array(array(
1677                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1678                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1679                 'header'        => 'From',
1680                 'key'           => 'info@example.org',
1681             )),
1682             'enabled'       => 1,
1683         ));
1684
1685         $this->_sieveTestHelper($ruleData);
1686     }
1687
1688     /**
1689      * @see 0006222: Keep a copy from mails forwarded to another emailaddress
1690      */
1691     public function testSetForwardRuleWithoutCopy()
1692     {
1693         $ruleData = array(array(
1694             'id'            => 1,
1695             'action_type'   => Felamimail_Sieve_Rule_Action::REDIRECT,
1696             'action_argument' => array(
1697                 'emails' => 'someaccount@example.org',
1698                 'copy'   => 0,
1699             ),
1700             'conjunction'     => 'allof',
1701             'conditions'    => array(array(
1702                 'test'          => Felamimail_Sieve_Rule_Condition::TEST_ADDRESS,
1703                 'comperator'    => Felamimail_Sieve_Rule_Condition::COMPERATOR_CONTAINS,
1704                 'header'        => 'From',
1705                 'key'           => 'info@example.org',
1706             )),
1707             'enabled'       => 1,
1708         ));
1709
1710         $this->_sieveTestHelper($ruleData);
1711     }
1712
1713     /**
1714      * testGetVacationTemplates
1715      *
1716      * @return array
1717      */
1718     public function testGetVacationTemplates()
1719     {
1720         $this->_addVacationTemplateFile();
1721         $result = $this->_json->getVacationMessageTemplates();
1722         
1723         $this->assertTrue($result['totalcount'] > 0, 'no templates found');
1724         $found = FALSE;
1725         foreach ($result['results'] as $template) {
1726             if ($template['name'] === $this->_sieveVacationTemplateFile) {
1727                 $found = TRUE;
1728                 break;
1729             }
1730         }
1731         
1732         $this->assertTrue($found, 'wrong templates: ' . print_r($result['results'], TRUE));
1733         
1734         return $template;
1735     }
1736     
1737     /**
1738      * add vacation template file to vfs
1739      */
1740     protected function _addVacationTemplateFile()
1741     {
1742         $webdavRoot = new DAV\ObjectTree(new Tinebase_WebDav_Root());
1743         $path = '/webdav/Felamimail/shared/Vacation Templates';
1744         $node = $webdavRoot->getNodeForPath($path);
1745         $this->_pathsToDelete[] = $path . '/' . $this->_sieveVacationTemplateFile;
1746         $node->createFile($this->_sieveVacationTemplateFile, fopen(dirname(__FILE__) . '/../files/' . $this->_sieveVacationTemplateFile, 'r'));
1747     }
1748     
1749     /**
1750      * testGetVacationMessage
1751      */
1752     public function testGetVacationMessage()
1753     {
1754         $result = $this->_getVacationMessageWithTemplate();
1755         $sclever = Tinebase_User::getInstance()->getFullUserByLoginName('sclever');
1756         $pwulf = Tinebase_User::getInstance()->getFullUserByLoginName('pwulf');
1757         $this->assertEquals("Ich bin vom 18.04.2012 bis zum 20.04.2012 im Urlaub. Bitte kontaktieren Sie<br /> Paul Wulf (" .
1758             $pwulf->accountEmailAddress . ") oder Susan Clever (" .
1759             $sclever->accountEmailAddress . ").<br /><br />I am on vacation until Apr 20, 2012. Please contact Paul Wulf<br />(" .
1760             $pwulf->accountEmailAddress . ") or Susan Clever (" .
1761             $sclever->accountEmailAddress . ") instead.<br /><br />" .
1762             Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId())->n_fn, $result['message']);
1763     }
1764     
1765     /**
1766      * get vacation message with template
1767      *
1768      * @return array
1769      */
1770     protected function _getVacationMessageWithTemplate()
1771     {
1772         $template = $this->testGetVacationTemplates();
1773         $sclever = Tinebase_User::getInstance()->getFullUserByLoginName('sclever');
1774         $result = $this->_json->getVacationMessage(array(
1775             'start_date' => '2012-04-18',
1776             'end_date'   => '2012-04-20',
1777             'contact_ids' => array(
1778                 Tinebase_User::getInstance()->getFullUserByLoginName('pwulf')->contact_id,
1779                 $sclever->contact_id,
1780             ),
1781             'template_id' => $template['id'],
1782             'signature' => $this->_account->signature
1783         ));
1784         
1785         return $result;
1786     }
1787     
1788     /**
1789      * testGetVacationWithSignature
1790      *
1791      * @see 0006866: check signature linebreaks in vacation message from template
1792      */
1793     public function testGetVacationWithSignature()
1794     {
1795         $this->_sieveVacationTemplateFile = 'vacation_template_sig.tpl';
1796         
1797         // set signature with <br> + linebreaks
1798         $this->_account->signature = "llalala<br>\nxyz<br>\nblubb<br>";
1799         
1800         $result = $this->_getVacationMessageWithTemplate();
1801         $this->assertContains('-- <br />llalala<br />xyz<br />blubb<br />', $result['message'], 'wrong linebreaks or missing signature');
1802     }
1803     
1804     /**
1805     * testSetVacationWithStartAndEndDate
1806     *
1807     * @see 0006266: automatic deactivation of vacation message
1808     */
1809     public function testSetVacationWithStartAndEndDate()
1810     {
1811         $vacationData = $this->_getVacationData();
1812         $vacationData['start_date'] = '2012-04-18';
1813         $vacationData['end_date'] = '2012-04-20';
1814         $result = $this->_sieveTestHelper($vacationData);
1815         
1816         $this->assertContains($vacationData['start_date'], $result['start_date']);
1817         $this->assertContains($vacationData['end_date'], $result['end_date']);
1818     }
1819     
1820     /**
1821      * testSieveRulesOrder
1822      *
1823      * @see 0007240: order of sieve rules changes when vacation message is saved
1824      */
1825     public function testSieveRulesOrder()
1826     {
1827         $this->_setTestScriptname();
1828         
1829         // disable vacation first
1830         $this->_setDisabledVacation();
1831         
1832         $sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
1833         
1834         $ruleData = $this->_getRuleData();
1835         $ruleData[0]['id'] = $ruleData[2]['id'];
1836         $ruleData[2]['id'] = 11;
1837         $resultSet = $this->_json->saveRules($this->_account->getId(), $ruleData);
1838         $sieveScriptRules = $sieveBackend->getScript($this->_testSieveScriptName);
1839         
1840         $this->_setDisabledVacation();
1841         $sieveScriptVacation = $sieveBackend->getScript($this->_testSieveScriptName);
1842         
1843         // compare sieve scripts
1844         $this->assertContains($sieveScriptRules, $sieveScriptVacation, 'rule order changed');
1845     }
1846
1847     public function testSieveEmailNotification()
1848     {
1849         $this->_setTestScriptname();
1850
1851         $this->_account->sieve_notification_email = 'test@test.de';
1852         Felamimail_Controller_Account::getInstance()->update($this->_account);
1853
1854         $script = new Felamimail_Sieve_Backend_Sql($this->_account->getId());
1855         $scriptParts = $script->getScriptParts();
1856         static::assertEquals(1, $scriptParts->count());
1857         /** @var Felamimail_Model_Sieve_ScriptPart $scriptPart */
1858         $scriptPart = $scriptParts->getFirstRecord();
1859         static::assertTrue(count(array_intersect(array('"enotify"', '"variables"', '"copy"', '"body"'),
1860             $scriptPart->xprops(Felamimail_Model_Sieve_ScriptPart::XPROPS_REQUIRES))) === 4,
1861             print_r($scriptPart->xprops(Felamimail_Model_Sieve_ScriptPart::XPROPS_REQUIRES), true));
1862         static::assertContains('test@test.de', $script->getSieve());
1863     }
1864
1865     /**
1866      * use another name for test sieve script
1867      */
1868     protected function _setTestScriptname()
1869     {
1870         $this->_oldActiveSieveScriptName = Felamimail_Controller_Sieve::getInstance()->getActiveScriptName($this->_account->getId());
1871         $this->_testSieveScriptName = 'Felamimail_Unittest';
1872         Felamimail_Controller_Sieve::getInstance()->setScriptName($this->_testSieveScriptName);
1873     }
1874     
1875     /**
1876      * set disabled vacation message
1877      */
1878     protected function _setDisabledVacation()
1879     {
1880         $vacationData = $this->_getVacationData();
1881         $vacationData['enabled'] = FALSE;
1882         $resultSet = $this->_json->saveVacation($vacationData);
1883     }
1884     
1885     /**
1886      * get folder filter
1887      *
1888      * @return array
1889      */
1890     protected function _getFolderFilter()
1891     {
1892         return array(array(
1893             'field' => 'globalname', 'operator' => 'equals', 'value' => ''
1894         ));
1895     }
1896
1897     /**
1898      * get message filter
1899      *
1900      * @param string $_folderId
1901      * @return array
1902      */
1903     protected function _getMessageFilter($_folderId)
1904     {
1905         $result = array(array(
1906             'field' => 'folder_id', 'operator' => 'equals', 'value' => $_folderId
1907         ));
1908         
1909         return $result;
1910     }
1911     
1912     /**
1913      * get mailbox
1914      *
1915      * @param string $name
1916      * @param boolean $createFolder
1917      * @return Felamimail_Model_Folder|NULL
1918      */
1919     protected function _getFolder($name, $createFolder = TRUE)
1920     {
1921         Felamimail_Controller_Cache_Folder::getInstance()->update($this->_account->getId());
1922         try {
1923             $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($this->_account->getId(), $name);
1924         } catch (Tinebase_Exception_NotFound $tenf) {
1925             $folder = ($createFolder) ? Felamimail_Controller_Folder::getInstance()->create($this->_account, $name) : NULL;
1926         }
1927         
1928         return $folder;
1929     }
1930
1931     /**
1932      * get message data
1933      *
1934      * @return array
1935      */
1936     protected function _getMessageData($_emailFrom = '', $_subject = 'test')
1937     {
1938         return array(
1939             'account_id'    => $this->_account->getId(),
1940             'subject'       => $_subject,
1941             'to'            => [Tinebase_Core::getUser()->accountEmailAddress],
1942             'body'          => 'aaaaaä <br>',
1943             'headers'       => array('X-Tine20TestMessage' => 'jsontest'),
1944             'from_email'    => $_emailFrom,
1945             'content_type'  => Felamimail_Model_Message::CONTENT_TYPE_HTML,
1946         );
1947     }
1948
1949     /**
1950      * send message and return message array
1951      *
1952      * @param string $folderName
1953      * @param array $addtionalHeaders
1954      * @return array
1955      */
1956     protected function _sendMessage(
1957         $folderName = 'INBOX',
1958         $additionalHeaders = array(),
1959         $_emailFrom = '',
1960         $_subject = 'test',
1961         $_messageToSend = null)
1962     {
1963         $messageToSend = $_messageToSend ? $_messageToSend : $this->_getMessageData($_emailFrom, $_subject);
1964         $messageToSend['headers'] = array_merge($messageToSend['headers'], $additionalHeaders);
1965         $this->_json->saveMessage($messageToSend);
1966         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
1967
1968         $i = 0;
1969         while ($i < 5) {
1970             $result = $this->_getMessages($folderName);
1971             $message = $this->_getMessageFromSearchResult($result, $messageToSend['subject']);
1972             if (! empty($message)) {
1973                 break;
1974             }
1975             // sleep for 1 sec because mailserver may be slower than expected
1976             sleep(1);
1977             $i++;
1978         }
1979
1980         $this->assertTrue(! empty($message), 'Sent message not found.');
1981         
1982         return $message;
1983     }
1984     
1985     /**
1986      * returns message array from result
1987      *
1988      * @param array $_result
1989      * @param string $_subject
1990      * @return array
1991      */
1992     protected function _getMessageFromSearchResult($_result, $_subject)
1993     {
1994         $message = array();
1995         foreach ($_result['results'] as $mail) {
1996             if ($mail['subject'] == $_subject) {
1997                 $message = $mail;
1998             }
1999         }
2000         
2001         return $message;
2002     }
2003     
2004     /**
2005      * get messages from folder
2006      *
2007      * @param string $_folderName
2008      * @return array
2009      */
2010     protected function _getMessages($_folderName = 'INBOX')
2011     {
2012         $folder = $this->_getFolder($_folderName);
2013         $filter = $this->_getMessageFilter($folder->getId());
2014         // update cache
2015         $folder = Felamimail_Controller_Cache_Message::getInstance()->updateCache($folder, 10, 1);
2016         $i = 0;
2017         while ($folder->cache_status != Felamimail_Model_Folder::CACHE_STATUS_COMPLETE && $i < 10) {
2018             $folder = Felamimail_Controller_Cache_Message::getInstance()->updateCache($folder, 10);
2019             $i++;
2020         }
2021         $result = $this->_json->searchMessages($filter, '');
2022
2023         return $result;
2024     }
2025     
2026     /**
2027      * search for message defined by subject in folder
2028      *
2029      * @param string $_subject
2030      * @param string $_folderName
2031      * @return string message data
2032      */
2033     protected function _searchForMessageBySubject($_subject, $_folderName = 'INBOX')
2034     {
2035         // give server some time to send and receive messages
2036         sleep(1);
2037
2038         $result = $this->_getMessages($_folderName);
2039         
2040         $message = array();
2041         foreach ($result['results'] as $mail) {
2042             if ($mail['subject'] == $_subject) {
2043                 $message = $mail;
2044             }
2045         }
2046         $this->assertGreaterThan(0, $result['totalcount'], 'folder is empty');
2047         $this->assertTrue(! empty($message), 'Message not found');
2048         
2049         return $message;
2050     }
2051     
2052     /**
2053      * sieve test helper
2054      *
2055      * @param array $_sieveData
2056      * @return array
2057      */
2058     protected function _sieveTestHelper($_sieveData, $_isMime = FALSE)
2059     {
2060         $this->_setTestScriptname();
2061         
2062         // check which save fn to use
2063         if ((isset($_sieveData['reason']) || array_key_exists('reason', $_sieveData))) {
2064             $resultSet = $this->_json->saveVacation($_sieveData);
2065             $this->assertEquals($this->_account->email, $resultSet['addresses'][0]);
2066             
2067             $_sieveBackend = Felamimail_Backend_SieveFactory::factory($this->_account->getId());
2068             
2069             if (preg_match('/dbmail/i', $_sieveBackend->getImplementation())) {
2070                 $translate = Tinebase_Translation::getTranslation('Felamimail');
2071                 $this->assertEquals(sprintf(
2072                     $translate->_('Out of Office reply from %1$s'), Tinebase_Core::getUser()->accountFullName),
2073                     $resultSet['subject']
2074                 );
2075             } else {
2076                 $this->assertEquals($_sieveData['subject'], $resultSet['subject']);
2077             }
2078             
2079             if ($_isMime) {
2080                 // TODO check why behaviour changed with php 7 (test was relaxed to hotfix this)
2081                 //$this->assertEquals(html_entity_decode('unittest vacation&nbsp;message', ENT_NOQUOTES, 'UTF-8'), $resultSet['reason']);
2082                 self::assertContains('unittest vacation', $resultSet['reason']);
2083             } else {
2084                 $this->assertEquals($_sieveData['reason'], $resultSet['reason']);
2085             }
2086             
2087         } else if ((isset($_sieveData[0]['action_type']) || array_key_exists('action_type', $_sieveData[0]))) {
2088             $resultSet = $this->_json->saveRules($this->_account->getId(), $_sieveData);
2089             $this->assertEquals($_sieveData, $resultSet);
2090         }
2091         
2092         return $resultSet;
2093     }
2094
2095     /**
2096      * search preferences by application felamimail
2097      *
2098      */
2099     public function testSearchFelamimailPreferences()
2100     {
2101         // search prefs
2102         $result = $this->_frontend->searchPreferencesForApplication('Felamimail', '');
2103         
2104         // check results
2105         $this->assertTrue(isset($result['results']));
2106         $this->assertGreaterThan(0, $result['totalcount']);
2107     }
2108     
2109     /**
2110      * testGetRegistryData
2111      *
2112      * @see 0010251: do not send unused config data to client
2113      */
2114     public function testGetRegistryData()
2115     {
2116         $regData = $this->_json->getRegistryData();
2117
2118         $this->assertFalse(isset($regData['defaults']));
2119         $supportedFlags = Felamimail_Config::getInstance()->featureEnabled(Felamimail_Config::FEATURE_TINE20_FLAG)
2120             ? 6
2121             : 5;
2122         $this->assertEquals($supportedFlags, $regData['supportedFlags']['totalcount']);
2123     }
2124
2125     /**
2126      * @see 0002284: add reply-to setting to email account
2127      */
2128     public function testReplyToSetting()
2129     {
2130         $this->_account->reply_to = 'noreply@tine20.org';
2131         $this->_json->saveAccount($this->_account->toArray());
2132
2133         $this->_foldersToClear[] = 'INBOX';
2134         $messageToSend = $this->_getMessageData();
2135         $this->_json->saveMessage($messageToSend);
2136         $message = $this->_searchForMessageBySubject($messageToSend['subject']);
2137
2138         $complete = $this->_json->getMessage($message['id']);
2139         $this->assertTrue(isset($complete['headers']['reply-to']), print_r($complete, true));
2140         $this->assertEquals('"Tine 2.0 Admin Account" <noreply@tine20.org>', $complete['headers']['reply-to']);
2141     }
2142
2143     /**
2144      * Its possible to choice the kind of attachment when adding it.
2145      *
2146      * type = tempfile: uploaded from harddisk, supposed to be a regular attachment
2147      *
2148      * @see 0012950: More attachment methods for mail
2149      */
2150     public function testAttachmentMethodAttachment()
2151     {
2152         $message = $this->_testAttachmentType('tempfile');
2153
2154         self::assertTrue(isset($message['attachments']), 'no attachment set: ' . print_r($message, true));
2155         self::assertEquals(1, count($message['attachments']), 'no attachment set: ' . print_r($message, true));
2156         self::assertEquals('foobar1.txt', $message['attachments'][0]['filename']);
2157         self::assertEquals(16, $message['attachments'][0]['size']);
2158     }
2159
2160     /**
2161      * Its possible to choice the kind of attachment when adding it.
2162      *
2163      * type = download_public: uploaded from harddisk, supposed to be a public download link
2164      *
2165      * @see 0012950: More attachment methods for mail
2166      */
2167     public function testAttachmentMethodPublicDownloadLinkUpload()
2168     {
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         $message = $this->_testAttachmentType('download_protected');
2202
2203         self::assertTrue(isset($message['attachments']), 'attachment set: ' . print_r($message, true));
2204         self::assertEquals(0, count($message['attachments']), 'attachment set: ' . print_r($message, true));
2205         self::assertContains('/download', $message['body'], 'no download link in body: ' . print_r($message, true));
2206
2207         // download link id is at the end of message body
2208         $downloadLinkId = trim(substr($message['body'], -46, 40));
2209         $dl = Filemanager_Controller_DownloadLink::getInstance()->get($downloadLinkId);
2210         self::assertTrue(Filemanager_Controller_DownloadLink::getInstance()->validatePassword($dl, 'test'));
2211     }
2212
2213     /**
2214      * Its possible to choice the kind of attachment when adding it.
2215      *
2216      * type = filenode: chosen from fm, thats why type -> file, but the filemanager file is supposed to be used as a regular attachment
2217      *
2218      * @see 0012950: More attachment methods for mail
2219      */
2220     public function testAttachmentMethodFilemanagerNode()
2221     {
2222         $message = $this->_testAttachmentType('filenode');
2223
2224         self::assertTrue(isset($message['attachments']), 'no attachment set: ' . print_r($message, true));
2225         self::assertEquals(1, count($message['attachments']), 'no attachment set: ' . print_r($message, true));
2226         self::assertEquals('test.txt', $message['attachments'][0]['filename']);
2227         self::assertEquals(16, $message['attachments'][0]['size']);
2228     }
2229
2230     /**
2231      * @param $type
2232      * @return array
2233      *
2234      * @throws Filemanager_Exception_NodeExists
2235      * @throws Tinebase_Exception_InvalidArgument
2236      */
2237     protected function _testAttachmentType($type)
2238     {
2239         $this->_foldersToClear = array('INBOX', $this->_account->sent_folder);
2240         $tempfile = $this->_createTempFile('foobar1.txt');
2241
2242         if (in_array($type, array('tempfile', 'download_public', 'download_protected'))) {
2243             // attach uploaded tempfile
2244             $attachment = array(
2245                 'tempFile' => $tempfile->toArray(),
2246                 'name' => 'foobar1.txt',
2247                 'size' => $tempfile->size,
2248                 'type' => 'text/plain',
2249                 'id' => 'eeabe57fd3712a9fe27a34df07cb44cab9e9afb3',
2250                 'attachment_type' => $type,
2251             );
2252
2253         } elseif (in_array($type, array('filenode', 'download_public_fm', 'download_protected_fm'))) {
2254             // attach existing file from filemanager
2255             $nodeController = Filemanager_Controller_Node::getInstance();
2256             $testPath = '/' . Tinebase_FileSystem::FOLDER_TYPE_PERSONAL . '/' . Tinebase_Core::getUser()->accountLoginName . '/testcontainer';
2257             $result = $nodeController->createNodes($testPath, Tinebase_Model_Tree_FileObject::TYPE_FOLDER, array(), FALSE);
2258             $personalNode = $result[0];
2259             $filepath = $personalNode->path . '/test.txt';
2260
2261             // create empty file first (like the js frontend does)
2262             $nodeController->createNodes($filepath, Tinebase_Model_Tree_FileObject::TYPE_FILE, array(), FALSE);
2263             $fmFile = $nodeController->createNodes(
2264                 $filepath,
2265                 Tinebase_Model_Tree_FileObject::TYPE_FILE,
2266                 (array)$tempfile->getId(), TRUE
2267             )->getFirstRecord();
2268
2269             $attachment = [ // chosen from fm, thats why type -> file, but the filemanager file is supposed to be used as a regular attachment
2270                 'name' => $fmFile->name,
2271                 'path' => $fmFile->path,
2272                 'size' => $fmFile->size,
2273                 'type' => $fmFile->contenttype,
2274                 'id' => $fmFile->getId(),
2275                 'attachment_type' => $type,
2276             ];
2277
2278         } else {
2279             throw new Tinebase_Exception_InvalidArgument('invalid type given');
2280         }
2281
2282         if (in_array($type, array('download_protected_fm', 'download_protected'))) {
2283             $attachment['password'] = 'test';
2284         }
2285
2286         $messageToSend = [
2287             'note' => null,
2288             'content_type' => 'text/html',
2289             'account_id' => $this->_account->id,
2290             'to' => [
2291                 $this->_account->email
2292             ],
2293             'cc' => [],
2294             'bcc' => [],
2295             'subject' => 'attachment test [' . $type . ']',
2296             'body' => 'foobar',
2297
2298             'attachments' => [ $attachment ],
2299             'from_email' => 'vagrant@example.org',
2300             'customfields' => [],
2301             'headers' => array('X-Tine20TestMessage' => 'jsontest'),
2302         ];
2303
2304         $this->_json->saveMessage($messageToSend);
2305         $message = $this->_searchForMessageBySubject($messageToSend['subject']);
2306         $complete = $this->_json->getMessage($message['id']);
2307
2308         return $complete;
2309     }
2310
2311     /**
2312      * @param string $tempfileName
2313      * @return Tinebase_Model_TempFile
2314      */
2315     protected function _createTempFile($tempfileName = 'test.txt')
2316     {
2317         $tempfilePath = Tinebase_Core::getTempDir() . DIRECTORY_SEPARATOR . $tempfileName;
2318         file_put_contents($tempfilePath, 'some content');
2319         return Tinebase_TempFile::getInstance()->createTempFile($tempfilePath, $tempfileName);
2320     }
2321 }