/** * Returns the transport with the server/auth information from the specified * account, along with the configuration's smtp client name for using when * helo'ing the mail server. * * @param Conjoon_Modules_Groupware_Email_Account $account The account for * which the transport should be returned * * @return Conjoon\Mail\Transport\Smtp */ protected function getTransportForAccount(\Conjoon_Modules_Groupware_Email_Account $account) { $regCon = $this->getApplicationConfiguration(); $clientName = $regCon->application->mail->smtp->client_name; $config = array(); if ($account->isOutboxAuth()) { $config = array('name' => $clientName, 'auth' => 'login', 'username' => $account->getUsernameOutbox(), 'password' => $account->getPasswordOutbox(), 'port' => $account->getPortOutbox()); $ssl = $account->getOutboxConnectionType(); if ($ssl == 'SSL' || $ssl == 'TLS') { $config['ssl'] = $ssl; } } /** * @see \Conjoon\Mail\Transport\Smtp */ require_once 'Conjoon/Mail/Transport/Smtp.php'; return new \Conjoon\Mail\Transport\Smtp($account->getServerOutbox(), $config); }
/** * Saves a sent email into the database. * * @param Conjoon_Modules_Groupware_Email_Draft $message * @param Conjoon_Modules_Groupware_Email_Account $account * @param integer $userId * @param Conjoon_Mail_Sent $mailSent * @param string $type * @param integer $referencesId The id of the email that was refernced sending this * message. This argument will only be taken into account if $type euqals to * reply or reply_all * @param array $postedAttachments * @param array $removeAttachmentIds * * @return array the data from groupware_email_item associated with * the newly saved entry */ public function saveSentEmail(Conjoon_Modules_Groupware_Email_Draft $message, Conjoon_Modules_Groupware_Email_Account $account, $userId, Conjoon_Mail_Sent $mailSent, $type = "", $referencesId = -1, $postedAttachments = array(), $removeAttachmentIds = array()) { $mail = $mailSent->getMailObject(); $userId = (int) $userId; $accountId = (int) $account->getId(); $messageId = (int) $message->getId(); $referenceId = $referencesId <= 0 ? 0 : $referencesId; if ($userId <= 0 || $accountId <= 0) { return array(); } $referencesModel = new Conjoon_Modules_Groupware_Email_Item_Model_References(); $emailRecipientsFilter = new Conjoon_Filter_EmailRecipients(); $emailRecipientsToStringFilter = new Conjoon_Filter_EmailRecipientsToString(); $outboxModel = new Conjoon_Modules_Groupware_Email_Item_Model_Outbox(); $folderModel = new Conjoon_Modules_Groupware_Email_Folder_Model_Folder(); // first check the folder type of the email $folderId = $message->getGroupwareEmailFoldersId(); $messageType = 'scratch'; // negative/0, means the message was created from draft if ($folderId <= 0) { $messageType = 'scratch'; } else { // anything else needs the meta info type fetched out of the folder model $metaInfo = $folderModel->getMetaInfo($folderId); switch ($metaInfo) { case Conjoon_Modules_Groupware_Email_Folder_Model_Folder::META_INFO_OUTBOX: $messageType = 'outbox'; break; case Conjoon_Modules_Groupware_Email_Folder_Model_Folder::META_INFO_DRAFT: $messageType = 'draft'; break; // anything else is probably a reply or forward to an existing message in any other // folder // anything else is probably a reply or forward to an existing message in any other // folder default: $messageType = 'scratch'; break; } } // adjust the message type depending on the type if ($type == 'reply' || $type == 'reply_all' || $type == 'forward') { $messageType = 'scratch'; } // prefill update/insert arrays $sentFolderId = $folderModel->getSentFolder($accountId, $userId); $date = new Zend_Date($mail->getDate(), Zend_Date::RFC_2822); $replyTo = (string) $mail->getReplyTo(); $to = $message->getTo(); $cc = $message->getCc(); $bcc = $message->getBcc(); $fromAddress = new Conjoon_Modules_Groupware_Email_Address(array($account->getAddress(), $account->getUserName())); $toString = array(); foreach ($to as $recipient) { $toString[] = $recipient->__toString(); } $toString = implode(', ', $toString); $ccString = array(); foreach ($cc as $recipient) { $ccString[] = $recipient->__toString(); } $ccString = implode(', ', $ccString); $bccString = array(); foreach ($bcc as $recipient) { $bccString[] = $recipient->__toString(); } $bccString = implode(', ', $bccString); $outboxUpdate = array('sent_timestamp' => time(), 'raw_header' => $mailSent->getSentHeaderText(), 'raw_body' => $mailSent->getSentBodyText(), 'groupware_email_accounts_id' => $message->getGroupwareEmailAccountsId()); /** * @see Conjoon_Filter_DateToUtc */ require_once 'Conjoon/Filter/DateToUtc.php'; $filterToUtc = new Conjoon_Filter_DateToUtc(); $itemUpdate = array('reply_to' => $replyTo, 'from' => $fromAddress->__toString(), 'recipients' => $emailRecipientsToStringFilter->filter($emailRecipientsFilter->filter(array($toString, $ccString, $bccString))), 'sender' => $emailRecipientsToStringFilter->filter($emailRecipientsFilter->filter(array($fromAddress->__toString()))), 'groupware_email_folders_id' => $sentFolderId, 'date' => $filterToUtc->filter($date->get(Zend_Date::ISO_8601))); switch ($messageType) { // if the message was sent from an opened draft or from the outbox, // we simply can create a new entry in the tables, // as if it was created from scratch // if, however, the email was sent from drafts, a user might have updated // the addresses, the subject, the email text and the attachments. // those fields have to be updated in the datastorage as well case 'draft': Conjoon_Util_Array::apply($itemUpdate, array('subject' => $message->getSubject(), 'to' => $toString, 'cc' => $ccString, 'bcc' => $bccString, 'content_text_plain' => $message->getContentTextPlain(), 'content_text_html' => $message->getContentTextHtml())); $this->saveAttachmentsForDraft($message, $postedAttachments, $removeAttachmentIds); // most simple: mesageType is outbox which means we have simply to update a few fields // most simple: mesageType is outbox which means we have simply to update a few fields case 'outbox': if ($messageId <= 0 || $sentFolderId == 0) { return array(); } // the message might have referenced an item when it was created. // look up entry in reference table and set this to is_pending = false $referencesWhere = $referencesModel->getAdapter()->quoteInto('groupware_email_items_id = ?', $messageId) . ' AND ' . $referencesModel->getAdapter()->quoteInto('user_id = ?', $userId); $referencesModel->update(array('is_pending' => 0), $referencesWhere); $outboxWhere = $outboxModel->getAdapter()->quoteInto('groupware_email_items_id = ?', $messageId); $outboxModel->update($outboxUpdate, $outboxWhere); $itemWhere = $this->getAdapter()->quoteInto('id = ?', $messageId); $this->update($itemUpdate, $itemWhere); return $this->getItemForUser($messageId, $userId); break; // if the message was created from scratch, i.e. has no id and no folderId, // save a fresh row into the tables groupware_email_items_id, groupware_email_items_flags, // groupware_email_items_outbox // if the message was created from scratch, i.e. has no id and no folderId, // save a fresh row into the tables groupware_email_items_id, groupware_email_items_flags, // groupware_email_items_outbox case 'scratch': Conjoon_Util_Array::apply($itemUpdate, array('subject' => $message->getSubject(), 'to' => $toString, 'cc' => $ccString, 'bcc' => $bccString, 'in_reply_to' => $message->getInReplyTo(), 'references' => $message->getReferences(), 'content_text_plain' => $message->getContentTextPlain(), 'content_text_html' => $message->getContentTextHtml())); $messageId = (int) $this->insert($itemUpdate); if ($messageId <= 0) { return array(); } $flagModel = new Conjoon_Modules_Groupware_Email_Item_Model_Flag(); $referenceType = ''; if (($type == Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_REPLY || $type == Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_REPLY_ALL || $type == Conjoon_Modules_Groupware_Email_Keys::REFERENCE_TYPE_FORWARD) && $referenceId != 0) { $referenceType = $type; $referenceUpdate = array('groupware_email_items_id' => $messageId, 'user_id' => $userId, 'reference_items_id' => $referenceId, 'reference_type' => $referenceType); $referencesModel->insert($referenceUpdate); } $flagUpdate = array('groupware_email_items_id' => $messageId, 'user_id' => $userId, 'is_read' => 1, 'is_spam' => 0, 'is_deleted' => 0); $flagModel->insert($flagUpdate); Conjoon_Util_Array::apply($outboxUpdate, array('groupware_email_items_id' => $messageId)); $outboxModel->insert($outboxUpdate); $message->setId($messageId); $this->saveAttachmentsForDraft($message, $postedAttachments, $removeAttachmentIds); return $this->getItemForUser($messageId, $userId); break; } return null; }
/** * @param int $userId The id of the user to process the email-accounts for. * @param int $accountId The id of the account to fetch the emails for * * @return Array An associative array with the keys of the fetched and saved * emails in the array 'fetched', and error-messages in the key 'errors'. */ private function _fetchEmails($userId, Conjoon_Modules_Groupware_Email_Account $account) { $fetchedEmailIds = array(); $fetchedEmailErrors = array(); $userId = (int) $userId; if ($userId <= 0) { return $fetchedEmailIds; } $transports = array(Conjoon_Modules_Groupware_Email_Account::PROTOCOL_POP3 => "Conjoon_Mail_Storage_Pop3", Conjoon_Modules_Groupware_Email_Account::PROTOCOL_IMAP => "Conjoon_Mail_Storage_Imap"); self::_setIconvEncoding(self::ICONV_UTF_8); $accountId = $account->getId(); $transport = $transports[$account->getProtocol()]; $isPop3 = $account->getProtocol() == Conjoon_Modules_Groupware_Email_Account::PROTOCOL_POP3; $isCopyLeftOnServer = $account->isCopyLeftOnServer(); $cconf = array('host' => $account->getServerInbox(), 'port' => $account->getPortInbox(), 'user' => $account->getUsernameInbox(), 'password' => $account->getPasswordInbox()); $ssl = $account->getInboxConnectionType(); if ($ssl == 'SSL' || $ssl == 'TLS') { $cconf['ssl'] = $ssl; } $mail = new $transport($cconf); $hasUniqueId = $mail->hasUniqueId; $mailCount = count($mail); if ($hasUniqueId) { $uidl = $mail->getUniqueId(); $this->_cacheUidl($uidl, $accountId, $this->_computeChunkSize($mailCount)); // this is to prevent undefined indexes when the count of uidl // differs from the number of emails fetched. This is a very rare error // that occures now and then - see http://wiki.conjoon.org/ticket/189 // it's assumed its related to connection aborts during communication // with the mail server if (count($uidl) != $mailCount) { return array('fetched' => $fetchedEmailIds, 'errors' => array('Could not retrieve messages - number of items in unique id list ' . 'differs from total number of emails on the server: ' . 'Number of unique ids: ' . count($uidl) . '; number of messages: ' . $mailCount . '; ' . 'This is possibly related to a connection abort while attempting to fetch ' . 'messages from a server. Please try again.')); } } $messagesToRemove = array(); for ($oo = 1; $oo < $mailCount + 1; $oo++) { $messageNum = $oo; $this->_attachmentCounter = 1; $emailItem = array(); $rawHeader = ""; $rawBody = ""; // check if the account supports UIDL, and skip the message // if it is already available in the db if ($hasUniqueId) { if ($this->_isUidPresent($accountId, $uidl[$oo]) === true) { if (!$isCopyLeftOnServer && $isPop3) { $messagesToRemove[] = $messageNum; } continue; } else { $emailItem['uid'] = $uidl[$oo]; } } // check here if we can process the message, taking memory limit // of php ini into account if (!$this->_maxMemory) { $this->_maxMemory = Conjoon_Util_Format::convertToBytes(ini_get('memory_limit')); } $s = $mail->getSize($messageNum); if ($s == 0) { $fetchedEmailErrors[] = 'Could not save message No. ' . $messageNum . ' - message size seems to be 0 bytes'; continue; } if ($this->_maxMemory / $s <= 17) { $fetchedEmailErrors[] = 'Could not save message No. ' . $messageNum . ' - message could exceed available memory size (' . $this->_maxMemory . ' bytes, message size ' . $s . ').'; continue; } self::_splitMessage($mail->getRawMessage($messageNum), $rawHeader, $rawBody); $message = new Conjoon_Mail_Message(array('headers' => $rawHeader, 'noToplines' => true, 'content' => $rawBody)); $messageId = ""; try { $messageId = $message->messageId; } catch (Zend_Mail_Exception $e) { // ignore } $emailItem['messageId'] = $messageId; $mail->noop(); // check here if we can remove the mail from the server // check first if UIDL is supported. if not, look up the // message if (!$hasUniqueId) { $id = $this->_isMessageIdPresent($messageId, $accountId, $rawHeader, $rawBody); $mail->noop(); if ($id === true) { if (!$isCopyLeftOnServer && $isPop3) { $messagesToRemove[] = $messageNum; } continue; } } else { if (!$isCopyLeftOnServer && $isPop3) { $messagesToRemove[] = $messageNum; } } $mail->noop(); $emailItem['attachments'] = array(); $emailItem['userId'] = $userId; try { $emailItem['from'] = $message->from; } catch (Zend_Mail_Exception $e) { // may be changed to localized header values by anti vir programs try { $emailItem['from'] = $message->von; } catch (Zend_Mail_Exception $e) { $emailItem['from'] = "-"; } } if (!isset($emailItem['from'])) { throw new Zend_Mail_Exception("No header with the name \"from\" found. Please check if you have an anti virus program runnning in the background. Some are known to change the header values to localized derivates."); } $emailItem['subject'] = ""; // very few emails will come in without a subject. try { $emailItem['subject'] = $message->subject; } catch (Zend_Mail_Exception $e) { try { // may be changed to localized header values by anti vir programs $emailItem['subject'] = $message->betreff; } catch (Zend_Mail_exception $e) { // ignore } } catch (Zend_Mail_exception $e) { // ignore } $emailItem['date'] = ""; // date field will be given presedence try { $emailItem['date'] = $message->date; } catch (Zend_Mail_Exception $e) { // ignore } // if date not found, look up deliveryDate if (!$emailItem['date']) { try { $emailItem['date'] = $message->deliveryDate; } catch (Zend_Mail_Exception $e) { // ignore } if (!$emailItem['date']) { try { // may be changed to localized header values by anti vir programs $emailItem['date'] = $message->datum; } catch (Zend_Mail_Exception $e) { // ignore } // and one further down to fall back to actual // date if none was found if (!$emailItem['date']) { /** * @see Zend_Date */ require_once 'Zend/Date.php'; $zd = new Zend_Date(); $emailItem['date'] = $zd->get(Zend_Date::RFC_2822); } } } try { $emailItem['to'] = $message->to; } catch (Zend_Mail_Exception $e) { // "to" might not be used, instead "cc" will be probably available // then $emailItem['to'] = ""; } if (!$emailItem['to']) { try { // may be changed to localized header values by anti vir programs $emailItem['to'] = $message->an; } catch (Zend_Mail_Exception $e) { // ignore } } try { $emailItem['cc'] = $message->cc; } catch (Zend_Mail_Exception $e) { $emailItem['cc'] = ''; } try { $emailItem['references'] = $message->references; } catch (Zend_Mail_Exception $e) { $emailItem['references'] = ''; } try { $emailItem['replyTo'] = $message->replyTo; } catch (Zend_Mail_Exception $e) { $emailItem['replyTo'] = ''; } try { $emailItem['inReplyTo'] = $message->inReplyTo; } catch (Zend_Mail_Exception $e) { $emailItem['inReplyTo'] = ''; } $encodingInformation = $this->_getEncodingInformation($message); $contentType = $encodingInformation['contentType']; $mail->noop(); try { switch ($contentType) { case 'text/plain': $emailItem['contentTextPlain'] = $this->_decode($message->getContent(), $encodingInformation); break; case 'text/html': $emailItem['contentTextHtml'] = $this->_decode($message->getContent(), $encodingInformation); break; case 'multipart/mixed': $this->_parseMultipartMixed($message, $emailItem); break; case 'multipart/alternative': $this->_parseMultipartAlternative($message, $emailItem); break; case 'multipart/related': $this->_parseMultipartRelated($message, $emailItem); break; case 'multipart/signed': $this->_parseMultipartSigned($message, $emailItem); break; case 'multipart/report': $this->_parseMultipartReport($message, $emailItem); break; default: $emailItem['contentTextPlain'] = $this->_decode($message->getContent(), $encodingInformation); break; } } catch (Exception $e) { $fetchedEmailErrors[] = "Could not save message No. " . $messageNum . " with the subject \"" . $emailItem['subject'] . "\". " . "An unexpected error occurred: \"" . $e->getMessage() . "\""; $mail->noop(); continue; } $mail->noop(); if (!isset($emailItem['contentTextPlain'])) { $emailItem['contentTextPlain'] = ''; } if (!isset($emailItem['contentTextHtml'])) { $emailItem['contentTextHtml'] = ''; } $this->_assignJunkStatus($userId, $emailItem); $this->_assignFolderId($userId, $accountId, $emailItem); $emailItem['rawHeader'] =& $rawHeader; $emailItem['rawBody'] =& $rawBody; $mail->noop(); if (!$emailItem['messageId']) { $emailItem['hash'] = Conjoon_Modules_Groupware_Email_Item_Model_Inbox::computeMessageHash($rawHeader, $rawBody); } $mail->noop(); $saved = $this->_saveEmail($emailItem, $userId); $mail->noop(); if (is_int($saved) > 0) { $fetchedEmailIds[] = $saved; } else { $fetchedEmailErrors[] = "Could not save Email Message with the " . " subject \"" . $emailItem['subject'] . "\"" . ", date \"" . $emailItem['date'] . "\"" . ": " . $saved; continue; } $mail->noop(); if (!$isCopyLeftOnServer && $isPop3) { $messagesToRemove[] = $messageNum; } } $messagesToRemove = array_unique($messagesToRemove); foreach ($messagesToRemove as $id) { $mail->removeMessage($id); } self::_setIconvEncoding(self::ICONV_OLD); return array('fetched' => $fetchedEmailIds, 'errors' => $fetchedEmailErrors); }