0008644: error when sending mail with note (wrong charset)
[tine20] / tine20 / Felamimail / Controller / Message / Send.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Felamimail
6  * @subpackage  Controller
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * @copyright   Copyright (c) 2011-2014 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * send message controller for Felamimail
14  *
15  * @package     Felamimail
16  * @subpackage  Controller
17  */
18 class Felamimail_Controller_Message_Send extends Felamimail_Controller_Message
19 {
20     /**
21      * holds the instance of the singleton
22      *
23      * @var Felamimail_Controller_Message_Send
24      */
25     private static $_instance = NULL;
26     
27     /**
28      * the constructor
29      *
30      * don't use the constructor. use the singleton
31      */
32     private function __construct() 
33     {
34         $this->_backend = new Felamimail_Backend_Cache_Sql_Message();
35     }
36     
37     /**
38      * don't clone. Use the singleton.
39      *
40      */
41     private function __clone() 
42     {
43     }
44     
45     /**
46      * the singleton pattern
47      *
48      * @return Felamimail_Controller_Message_Send
49      */
50     public static function getInstance() 
51     {
52         if (self::$_instance === NULL) {
53             self::$_instance = new Felamimail_Controller_Message_Send();
54         }
55         
56         return self::$_instance;
57     }
58     
59     /**
60      * send one message through smtp
61      * 
62      * @param Felamimail_Model_Message $_message
63      * @return Felamimail_Model_Message
64      * @throws Tinebase_Exception_SystemGeneric
65      */
66     public function sendMessage(Felamimail_Model_Message $_message)
67     {
68         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . 
69             ' Sending message with subject ' . $_message->subject . ' to ' . print_r($_message->to, TRUE));
70         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_message->toArray(), TRUE));
71         
72         // increase execution time (sending message with attachments can take a long time)
73         $oldMaxExcecutionTime = Tinebase_Core::setExecutionLifeTime(300); // 5 minutes
74         
75         $account = Felamimail_Controller_Account::getInstance()->get($_message->account_id);
76         try {
77             $this->_resolveOriginalMessage($_message);
78             $mail = $this->createMailForSending($_message, $account, $nonPrivateRecipients);
79             $this->_sendMailViaTransport($mail, $account, $_message, true, $nonPrivateRecipients);
80         } catch (Exception $e) {
81             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not send message: ' . $e);
82             $translation = Tinebase_Translation::getTranslation('Felamimail');
83             $message = sprintf($translation->_('Error: %s'), $e->getMessage());
84             $tesg = new Tinebase_Exception_SystemGeneric($message);
85             $tesg->setTitle($translation->_('Could not send message'));
86             throw $tesg;
87         }
88         
89         // reset max execution time to old value
90         Tinebase_Core::setExecutionLifeTime($oldMaxExcecutionTime);
91         
92         return $_message;
93     }
94     
95     /**
96      * places a Felamimail_Model_Message in original_id field of given message (if it had an original_id set)
97      * 
98      * @param Felamimail_Model_Message $_message
99      */
100     protected function _resolveOriginalMessage(Felamimail_Model_Message $_message)
101     {
102         if (! $_message->original_id || $_message->original_id instanceof Felamimail_Model_Message) {
103             return;
104         }
105         
106         $originalMessageId = $_message->original_id;
107         if (strpos($originalMessageId, '_') !== FALSE ) {
108             list($originalMessageId, $partId) = explode('_', $originalMessageId);
109         } else {
110             $partId = NULL;
111         }
112         
113         try {
114             $originalMessage = ($originalMessageId) ? $this->get($originalMessageId) : NULL;
115         } catch (Tinebase_Exception_NotFound $tenf) {
116             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
117                 . ' Did not find original message (' . $originalMessageId . ')');
118             $originalMessage = NULL;
119         }
120         
121         $_message->original_id      = $originalMessage;
122         $_message->original_part_id = $partId;
123     }
124     
125     /**
126      * save message in folder (target folder can be within a different account)
127      * 
128      * @param string|Felamimail_Model_Folder $_folder globalname or folder record
129      * @param Felamimail_Model_Message $_message
130      * @return Felamimail_Model_Message
131      */
132     public function saveMessageInFolder($_folder, $_message)
133     {
134         $sourceAccount = Felamimail_Controller_Account::getInstance()->get($_message->account_id);
135         
136         if (is_string($_folder) && ($_folder === $sourceAccount->templates_folder || $_folder === $sourceAccount->drafts_folder)) {
137             // make sure that system folder exists
138             $systemFolder = $_folder === $sourceAccount->templates_folder ? Felamimail_Model_Folder::FOLDER_TEMPLATES : Felamimail_Model_Folder::FOLDER_DRAFTS;
139             $folder = Felamimail_Controller_Account::getInstance()->getSystemFolder($sourceAccount, $systemFolder);
140         } else if ($_folder instanceof Felamimail_Model_Folder) {
141             $folder = $_folder;
142         } else {
143             $folder = Felamimail_Controller_Folder::getInstance()->getByBackendAndGlobalName($_message->account_id, $_folder);
144         }
145         
146         $targetAccount = ($_message->account_id == $folder->account_id) ? $sourceAccount : Felamimail_Controller_Account::getInstance()->get($folder->account_id);
147         
148         $mailToAppend = $this->createMailForSending($_message, $sourceAccount);
149         
150         $transport = new Felamimail_Transport();
151         $mailAsString = $transport->getRawMessage($mailToAppend, $this->_getAdditionalHeaders($_message));
152         $flags = ($folder->globalname === $targetAccount->drafts_folder) ? array(Zend_Mail_Storage::FLAG_DRAFT) : null;
153         
154         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . 
155             ' Appending message ' . $_message->subject . ' to folder ' . $folder->globalname . ' in account ' . $targetAccount->name);
156         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . 
157             ' ' . $mailAsString);
158         
159         Felamimail_Backend_ImapFactory::factory($targetAccount)->appendMessage(
160             $mailAsString,
161             Felamimail_Model_Folder::encodeFolderName($folder->globalname),
162             $flags
163         );
164         
165         return $_message;
166     }
167     
168     /**
169      * Bcc recipients need to be added separately because they are removed by default
170      * 
171      * @param Felamimail_Model_Message $message
172      * @return array
173      */
174     protected function _getAdditionalHeaders($message)
175     {
176         $additionalHeaders = ($message && ! empty($message->bcc)) ? array('Bcc' => $message->bcc) : array();
177         return $additionalHeaders;
178     }
179     
180     /**
181      * create new mail for sending via SMTP
182      * 
183      * @param Felamimail_Model_Message $_message
184      * @param Felamimail_Model_Account $_account
185      * @param array $_nonPrivateRecipients
186      * @return Tinebase_Mail
187      */
188     public function createMailForSending(Felamimail_Model_Message $_message, Felamimail_Model_Account $_account, &$_nonPrivateRecipients = array())
189     {
190         // create new mail to send
191         $mail = new Tinebase_Mail('UTF-8');
192         $mail->setSubject($_message->subject);
193         
194         $this->_setMailBody($mail, $_message);
195         $this->_setMailFrom($mail, $_account, $_message);
196         $_nonPrivateRecipients = $this->_setMailRecipients($mail, $_message);
197         $this->_setMailHeaders($mail, $_account, $_message);
198         
199         $this->_addAttachments($mail, $_message);
200         
201         return $mail;
202     }
203     
204     /**
205      * send mail via transport (smtp)
206      * 
207      * @param Zend_Mail $_mail
208      * @param Felamimail_Model_Account $_account
209      * @param boolean $_saveInSent
210      * @param Felamimail_Model_Message $_message
211      * @param array $_nonPrivateRecipients
212      */
213     protected function _sendMailViaTransport(Zend_Mail $_mail, Felamimail_Model_Account $_account, Felamimail_Model_Message $_message = null, $_saveInSent = false, $_nonPrivateRecipients = array())
214     {
215         $smtpConfig = $_account->getSmtpConfig();
216         if (! empty($smtpConfig) && (isset($smtpConfig['hostname']) || array_key_exists('hostname', $smtpConfig))) {
217             $transport = new Felamimail_Transport($smtpConfig['hostname'], $smtpConfig);
218             
219             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) {
220                 if (isset($smtpConfig['password'])) {
221                     unset($smtpConfig['password']);
222                 }
223                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
224                     . ' About to send message via SMTP with the following config: ' . print_r($smtpConfig, true));
225             }
226             
227             Tinebase_Smtp::getInstance()->sendMessage($_mail, $transport);
228             
229             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
230                 . ' successful.');
231             
232             // append mail to sent folder
233             if ($_saveInSent) {
234                 $this->_saveInSent($transport, $_account, $this->_getAdditionalHeaders($_message));
235             }
236             
237             if ($_message !== null) {
238                 // add reply/forward flags if set
239                 if (! empty($_message->flags) 
240                     && ($_message->flags == Zend_Mail_Storage::FLAG_ANSWERED || $_message->flags == Zend_Mail_Storage::FLAG_PASSED)
241                     && $_message->original_id instanceof Felamimail_Model_Message
242                 ) {
243                     Felamimail_Controller_Message_Flags::getInstance()->addFlags($_message->original_id, array($_message->flags));
244                 }
245     
246                 // add email notes to contacts (only to/cc)
247                 if ($_message->note) {
248                     $this->_addEmailNote($_nonPrivateRecipients, $_message->subject, $_message->getPlainTextBody());
249                 }
250             }
251         } else {
252             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not send message, no smtp config found.');
253         }
254     }
255     
256     /**
257      * add email notes to contacts with email addresses in $_recipients
258      *
259      * @param array $_recipients
260      * @param string $_subject
261      * 
262      * @todo add email home (when we have OR filters)
263      * @todo add link to message in sent folder?
264      */
265     protected function _addEmailNote($_recipients, $_subject, $_body)
266     {
267         $filter = new Addressbook_Model_ContactFilter(array(
268             array('field' => 'email', 'operator' => 'in', 'value' => $_recipients)
269             // OR: array('field' => 'email_home', 'operator' => 'in', 'value' => $_recipients)
270         ));
271         $contacts = Addressbook_Controller_Contact::getInstance()->search($filter);
272         
273         if (count($contacts)) {
274         
275             $translate = Tinebase_Translation::getTranslation($this->_applicationName);
276             
277             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Adding email notes to ' . count($contacts) . ' contacts.');
278             
279             $truncatedBody = (extension_loaded('mbstring')) ? mb_substr($_body, 0, 4096, 'UTF-8') : substr($_body, 0, 4096);
280             $noteText = $translate->_('Subject') . ':' . $_subject . "\n\n" . $translate->_('Body') . ': ' . $truncatedBody;
281             
282             try {
283                 foreach ($contacts as $contact) {
284                     $note = new Tinebase_Model_Note(array(
285                         'note_type_id'           => Tinebase_Notes::getInstance()->getNoteTypeByName('email')->getId(),
286                         'note'                   => $noteText,
287                         'record_id'              => $contact->getId(),
288                         'record_model'           => 'Addressbook_Model_Contact',
289                     ));
290                     
291                     Tinebase_Notes::getInstance()->addNote($note);
292                 }
293             } catch (Zend_Db_Statement_Exception $zdse) {
294                 Tinebase_Core::getLogger()->err(__METHOD__ . '::' . __LINE__ . ' Saving note failed: ' . $noteText);
295                 Tinebase_Exception::log($zdse);
296             }
297         } else {
298             Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Found no contacts to add notes to.');
299         }
300     }
301     
302     /**
303      * append mail to send folder
304      * 
305      * @param Felamimail_Transport $_transport
306      * @param Felamimail_Model_Account $_account
307      * @param array $_additionalHeaders
308      * @return void
309      */
310     protected function _saveInSent(Felamimail_Transport $_transport, Felamimail_Model_Account $_account, $_additionalHeaders = array())
311     {
312         try {
313             $mailAsString = $_transport->getRawMessage(NULL, $_additionalHeaders);
314             $sentFolder = Felamimail_Controller_Account::getInstance()->getSystemFolder($_account, Felamimail_Model_Folder::FOLDER_SENT);
315             
316             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ .
317                 ' About to save message in sent folder (' . $sentFolder->globalname . ') ...');
318             
319             Felamimail_Backend_ImapFactory::factory($_account)->appendMessage(
320                 $mailAsString,
321                 Felamimail_Model_Folder::encodeFolderName($sentFolder->globalname)
322             );
323             
324             Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ 
325                 . ' Saved sent message in "' . $sentFolder->globalname . '".'
326             );
327         } catch (Zend_Mail_Protocol_Exception $zmpe) {
328             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
329                 . ' Could not save sent message in "' . $sentFolder->globalname . '".'
330                 . ' Please check if a folder with this name exists.'
331                 . '(' . $zmpe->getMessage() . ')'
332             );
333         } catch (Zend_Mail_Storage_Exception $zmse) {
334             Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ 
335                 . ' Could not save sent message in "' . $sentFolder->globalname . '".'
336                 . ' Please check if a folder with this name exists.'
337                 . '(' . $zmse->getMessage() . ')'
338             );
339         }
340     }
341     
342     /**
343      * send Zend_Mail message via smtp
344      * 
345      * @param  mixed      $accountId
346      * @param  Zend_Mail  $mail
347      * @param  boolean    $saveInSent
348      * @param  Felamimail_Model_Message $originalMessage
349      * @return Zend_Mail
350      */
351     public function sendZendMail($accountId, Zend_Mail $mail, $saveInSent = false, $originalMessage = NULL)
352     {
353         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . 
354             ' Sending message with subject ' . $mail->getSubject() 
355         );
356         if ($originalMessage !== NULL) {
357             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . 
358                 ' Original Message subject: ' . $originalMessage->subject . ' / Flag to set: ' . var_export($originalMessage->flags, TRUE)
359             );
360             
361             // this is required for adding the reply/forward flag in _sendMailViaTransport()
362             $originalMessage->original_id = $originalMessage;
363         }
364         
365         // increase execution time (sending message with attachments can take a long time)
366         $oldMaxExcecutionTime = Tinebase_Core::setExecutionLifeTime(300); // 5 minutes
367         
368         // get account
369         $account = ($accountId instanceof Felamimail_Model_Account) ? $accountId : Felamimail_Controller_Account::getInstance()->get($accountId);
370         
371         $this->_setMailFrom($mail, $account);
372         $this->_setMailHeaders($mail, $account);
373         $this->_sendMailViaTransport($mail, $account, $originalMessage, $saveInSent);
374         
375         // reset max execution time to old value
376         Tinebase_Core::setExecutionLifeTime($oldMaxExcecutionTime);
377         
378         return $mail;
379     }
380     
381     /**
382      * set mail body
383      * 
384      * @param Tinebase_Mail $_mail
385      * @param Felamimail_Model_Message $_message
386      */
387     protected function _setMailBody(Tinebase_Mail $_mail, Felamimail_Model_Message $_message)
388     {
389         if ($_message->content_type == Felamimail_Model_Message::CONTENT_TYPE_HTML) {
390             $_mail->setBodyHtml(Felamimail_Message::addHtmlMarkup($_message->body));
391             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . $_mail->getBodyHtml(TRUE));
392         }
393         
394         $plainBodyText = $_message->getPlainTextBody();
395         $_mail->setBodyText($plainBodyText);
396         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . $_mail->getBodyText(TRUE));
397     }
398     
399     /**
400      * set from in mail to be sent
401      * 
402      * @param Tinebase_Mail $_mail
403      * @param Felamimail_Model_Account $_account
404      * @param Felamimail_Model_Message $_message
405      */
406     protected function _setMailFrom(Zend_Mail $_mail, Felamimail_Model_Account $_account, Felamimail_Model_Message $_message = NULL)
407     {
408         $_mail->clearFrom();
409         
410         $from = (isset($_account->from) && ! empty($_account->from)) 
411             ? $_account->from 
412             : Tinebase_Core::getUser()->accountFullName;
413         
414         $email = ($_message !== NULL && ! empty($_message->from_email)) ? $_message->from_email : $_account->email;
415         
416         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Set from for mail: ' . $email . ' / ' . $from);
417         
418         $_mail->setFrom($email, $from);
419     }
420     
421     /**
422      * set mail recipients
423      * 
424      * @param Tinebase_Mail $_mail
425      * @param Felamimail_Model_Message $_message
426      * @return array
427      */
428     protected function _setMailRecipients(Zend_Mail $_mail, Felamimail_Model_Message $_message)
429     {
430         $nonPrivateRecipients = array();
431         $punycodeConverter = $this->getPunycodeConverter();
432         
433         foreach (array('to', 'cc', 'bcc') as $type) {
434             if (isset($_message->{$type})) {
435                 foreach((array) $_message->{$type} as $address) {
436                     
437                     $address = $punycodeConverter->encode($address);
438                     
439                     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' Add ' . $type . ' address: ' . $address);
440                     
441                     switch($type) {
442                         case 'to':
443                             $_mail->addTo($address);
444                             $nonPrivateRecipients[] = $address;
445                             break;
446                         case 'cc':
447                             $_mail->addCc($address);
448                             $nonPrivateRecipients[] = $address;
449                             break;
450                         case 'bcc':
451                             $_mail->addBcc($address);
452                             break;
453                     }
454                 }
455             }
456         }
457         
458         return $nonPrivateRecipients;
459     }
460     
461     /**
462      * set headers in mail to be sent
463      * 
464      * @param Tinebase_Mail $_mail
465      * @param Felamimail_Model_Account $_account
466      * @param Felamimail_Model_Message $_message
467      */
468     protected function _setMailHeaders(Zend_Mail $_mail, Felamimail_Model_Account $_account, Felamimail_Model_Message $_message = NULL)
469     {
470         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Setting mail headers');
471         
472         // add user agent
473         $_mail->addHeader('User-Agent', 'Tine 2.0 Email Client (version ' . TINE20_CODENAME . ' - ' . TINE20_PACKAGESTRING . ')');
474         
475         // set organization
476         if (isset($_account->organization) && ! empty($_account->organization)) {
477             $_mail->addHeader('Organization', $_account->organization);
478         }
479         
480         // set message-id (we could use Zend_Mail::createMessageId() here)
481         if ($_mail->getMessageId() === NULL) {
482             $domainPart = substr($_account->email, strpos($_account->email, '@'));
483             $uid = Tinebase_Record_Abstract::generateUID();
484             $_mail->setMessageId('<' . $uid . $domainPart . '>');
485         }
486         
487         if ($_message !== NULL) {
488             if ($_message->flags && $_message->flags == Zend_Mail_Storage::FLAG_ANSWERED && $_message->original_id instanceof Felamimail_Model_Message) {
489                 $this->_addReplyHeaders($_message);
490             }
491             
492             // set the header request response
493             if ($_message->reading_conf) {
494                 $_mail->addHeader('Disposition-Notification-To', $_message->from_email);
495             }
496             
497             // add other headers
498             if (! empty($_message->headers) && is_array($_message->headers)) {
499                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
500                     . ' Adding custom headers: ' . print_r($_message->headers, TRUE));
501                 foreach ($_message->headers as $key => $value) {
502                     $value = $this->_trimHeader($key, $value);
503                     $_mail->addHeader($key, $value);
504                 }
505             }
506         }
507     }
508     
509     /**
510      * trim message headers (Zend_Mail only supports < 998 chars)
511      * 
512      * @param string $value
513      * @return string
514      */
515     protected function _trimHeader($key, $value)
516     {
517         if (strlen($value) + strlen($key) > 998) {
518             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
519                 . ' Trimming header ' . $key);
520             
521             $value = substr(trim($value), 0, (995 - strlen($key)));
522
523             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
524                 . $value);
525         }
526         
527         return $value;
528     }
529     
530     /**
531      * set In-Reply-To and References headers
532      * 
533      * @param Felamimail_Model_Message $message
534      * 
535      * @see http://www.faqs.org/rfcs/rfc2822.html / Section 3.6.4.
536      */
537     protected function _addReplyHeaders(Felamimail_Model_Message $message)
538     {
539         $originalHeaders = Felamimail_Controller_Message::getInstance()->getMessageHeaders($message->original_id);
540         if (! isset($originalHeaders['message-id'])) {
541             // no message-id -> skip this
542             return;
543         }
544
545         $messageHeaders = is_array($message->headers) ? $message->headers : array();
546         $messageHeaders['In-Reply-To'] = $originalHeaders['message-id'];
547         
548         $references = '';
549         if (isset($originalHeaders['references'])) {
550             $references = $originalHeaders['references'] . ' ';
551         } else if (isset($originalHeaders['in-reply-to'])) {
552             $references = $originalHeaders['in-reply-to'] . ' ';
553         }
554         $references .= $originalHeaders['message-id'];
555         $messageHeaders['References'] = $references;
556         
557         $message->headers = $messageHeaders;
558     }
559     
560     /**
561      * add attachments to mail
562      *
563      * @param Tinebase_Mail $_mail
564      * @param Felamimail_Model_Message $_message
565      * @throws Felamimail_Exception_IMAP
566      */
567     protected function _addAttachments(Tinebase_Mail $_mail, Felamimail_Model_Message $_message)
568     {
569         if (! isset($_message->attachments) || empty($_message->attachments)) {
570             return;
571         }
572
573         $maxAttachmentSize = $this->_getMaxAttachmentSize();
574         $size = 0;
575         $tempFileBackend = Tinebase_TempFile::getInstance();
576         foreach ($_message->attachments as $attachment) {
577             if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
578                 . ' Adding attachment: ' . (is_object($attachment) ? print_r($attachment->toArray(), TRUE) : print_r($attachment, TRUE)));
579             
580             if (isset($attachment['type']) && $attachment['type'] == Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822 && $_message->original_id instanceof Felamimail_Model_Message) {
581                 $part = $this->getMessagePart($_message->original_id, ($_message->original_part_id) ? $_message->original_part_id : NULL);
582                 $part->decodeContent();
583                 
584                 $name = $attachment['name'] . '.eml';
585                 $type = $attachment['type'];
586                 if (! empty($attachment['size'])) {
587                     $size += $attachment['size'];
588                 }
589                 
590             } else {
591                 $tempFile = ($attachment instanceof Tinebase_Model_TempFile) 
592                     ? $attachment 
593                     : (((isset($attachment['tempFile']) || array_key_exists('tempFile', $attachment))) ? $tempFileBackend->get($attachment['tempFile']['id']) : NULL);
594                 
595                 if ($tempFile === NULL) {
596                     continue;
597                 }
598                 
599                 if (! $tempFile->path) {
600                     Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Could not find attachment.');
601                     continue;
602                 }
603                 
604                 // get contents from uploaded file
605                 $stream = fopen($tempFile->path, 'r');
606                 $part = new Zend_Mime_Part($stream);
607                 
608                 // RFC822 attachments are not encoded, set all others to ENCODING_BASE64
609                 $part->encoding = ($tempFile->type == Felamimail_Model_Message::CONTENT_TYPE_MESSAGE_RFC822) ? null : Zend_Mime::ENCODING_BASE64;
610                 
611                 $name = $tempFile->name;
612                 $type = $tempFile->type;
613                 
614                 if (! empty($tempFile->size)) {
615                     $size += $tempFile->size;
616                 }
617             }
618             
619             $part->setTypeAndDispositionForAttachment($type, $name);
620             
621             if ($size > $maxAttachmentSize) {
622                 if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__
623                     . ' Current attachment size: ' . convertToMegabytes($size) . ' MB / allowed size: ' . convertToMegabytes($maxAttachmentSize) . ' MB');
624                 throw new Felamimail_Exception_IMAP('Maximum attachment size exceeded. Please remove one or more attachments.');
625             }
626             
627             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
628                 . ' Adding attachment ' . $part->type);
629             
630             $_mail->addAttachment($part);
631         }
632     }
633     
634     /**
635      * get max attachment size for outgoing mails
636      * 
637      * - currently it is set to memory_limit / 10
638      * - returns size in Bytes
639      * 
640      * @return integer
641      */
642     protected function _getMaxAttachmentSize()
643     {
644         $configuredMemoryLimit = ini_get('memory_limit');
645         
646         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
647             . ' memory_limit = ' . $configuredMemoryLimit);
648         
649         if ($configuredMemoryLimit === FALSE or $configuredMemoryLimit == -1) {
650             // set to a big default value
651             $configuredMemoryLimit = '512M';
652         }
653         
654         return convertToBytes($configuredMemoryLimit) / 10;
655     }
656 }