/** * Test that only no duplicates can exist. */ public function testDuplicateEntriesInSameFieldAreOverWritten() { $list = new Swift_RecipientList(); $list->addCc(new Swift_Address("*****@*****.**")); $list->addCc("*****@*****.**", "Joe"); $list->addCc("*****@*****.**"); $list->addCc("*****@*****.**", "Foo"); $this->assertEqual(3, count($list->getCc())); }
/** * Send a mail, using params passed : * * - connection: * type (*) * params * - from (*) is an address * - reply-to is an address or an array of addresses * - return-path is an address * - to (*) is an address or an array of addresses * - cc is an address or an array of addresses * - bcc is an address or an array of addresses * - subject-template * - subject (*) * - body (*) can also be a direct string * content (*) * content-type (default is text/plain) * encoding * charset * - parts is an array of bodies * - attachments * - embed-images * * body[content] can be a direct string (which will be the body's content), or an associative array : * - type (partial or component) * - name is the name of the partial or component, as "module/partialName" or "module/componentName" * - vars is an associative array of variables, passed to the view * * attachments is an array of attachment, an attachement is either a string (path name), or a list of these options : * - path (*) is the real path on filesystem * - filename * - mime-type * * an address can be either : * - a string as an email address or in the "name <email>" format * - an array of two strings [name, email] or [email, name] * * embed-images is an associative array of key => image light path. * You can use "%%IMG_name-of-image%%" in the body or in any part to reference the corresponding embedded image. * * @param array $options * @throws Exception * @return int The number of successful recipients */ protected static function _send(array $options) { $options = array_merge(sfConfig::get('app_mailer_defaults', array()), $options); // Mailer if (!isset($options['connection'])) { throw new Exception('Connection configuration required'); } if (!isset($options['connection']['type'])) { throw new Exception('Connection type undefined'); } if (!isset($options['connection']['params'])) { $options['connection']['params'] = array(); } $connection = self::getConnection($options['connection']['type'], $options['connection']['params']); $mailer = new Swift($connection); $to = new Swift_RecipientList(); $to->addTo(self::getSwiftAddresses($options['to'])); // Basic elements $from = self::getSwiftAddress($options['from']); if (!isset($options['subject'])) { throw new Exception('Subject required'); } if (!isset($options['subject-template'])) { $options['subject-template'] = '%s'; } if (!isset($options['i18n-catalogue'])) { $options['i18n-catalogue'] = 'messages'; } $subject = self::getI18NString($options['subject'], $options['subject-template'], $options['i18n-catalogue']); // Message to be sent $mail = new Swift_Message($subject); // Embedded images if (isset($options['embed-images'])) { $embedded_images = self::embedImages($mail, @$options['embed-images']); } else { $embedded_images = array(); } // Get body as the main part if (!isset($options['body'])) { throw new Exception('Body is required'); } if (!is_array($options['body'])) { $options['body'] = array('content' => $options['body']); } $body = self::getPart($options['body'], $embedded_images); // Attach files if (isset($options['attachments']) && is_array($options['attachments'])) { // Known bug : When we have attachments, we must have body declared as a part, or the // mail will be received with no body. We fix this here : if (!isset($options['parts'])) { $options['parts'] = array(); } foreach ($options['attachments'] as $attachment) { $mail->attach(self::getAttachment($attachment)); } } // Attach parts (body is the first one) if (isset($options['parts']) && is_array($options['parts'])) { $parts = self::getParts($options['parts'], $embedded_images); array_unshift($parts, $body); foreach ($parts as $part) { $mail->attach($part); } } else { $mail->setBody($body->getData()); $mail->setCharset($body->getCharset()); $mail->setEncoding($body->getEncoding()); $mail->setContentType($body->getContentType()); } // Handle other options if (isset($options['bcc'])) { $to->addBcc(self::getSwiftAddresses($options['bcc'])); } if (isset($options['cc'])) { $to->addCc(self::getSwiftAddresses($options['cc'])); } if (isset($options['reply-to'])) { $mail->setReplyTo(self::getSwiftAddresses($options['reply-to'])); } if (isset($options['return-path'])) { $mail->setReturnPath(self::getSwiftAddress($options['return-path'])); } try { // Try to send the mail $result = $mailer->send($mail, $to, $from); $mailer->disconnect(); return $result; } catch (Exception $e) { // An error occured, disconnect an eventual connection, and forwards the exception $mailer->disconnect(); throw $e; } }
private function sendEmail($notificationIds, $swift, $userId, $emailContent, $cc) { // Use a transaction to allow us to prevent the email sending and marking of notification as done // getting out of step $this->db->begin(); try { $this->db->set('acknowledged', 't')->from('notifications')->in('id', $notificationIds)->update(); $email_config = Kohana::config('email'); $userResults = $this->db->select('people.email_address')->from('people')->join('users', 'users.person_id', 'people.id')->where('users.id', $userId)->limit(1)->get(); if (!isset($email_config['address'])) { self::msg('Email address not provided in email configuration', 'error'); return; } foreach ($userResults as $user) { $message = new Swift_Message(kohana::lang('misc.notification_subject', kohana::config('email.server_name')), $emailContent, 'text/html'); $recipients = new Swift_RecipientList(); $recipients->addTo($user->email_address); $cc = explode(',', $cc); foreach ($cc as $ccEmail) { $recipients->addCc(trim($ccEmail)); } // send the email $swift->send($message, $recipients, $email_config['address']); kohana::log('info', 'Email notification sent to ' . $user->email_address); } } catch (Exception $e) { // Email not sent, so undo marking of notification as complete. $this->db->rollback(); throw $e; } $this->db->commit(); }
function gmail(&$error, $to, $subject, $body, $fromName = "", $html = 0, $repto = "", $from = "", $cc = "", $reptoName = "") { global $errorReportingLevel; require_once GORUM_DIR . "/SwiftMailer/Swift.php"; $_S =& new AppSettings(); error_reporting(0); $log =& Swift_LogContainer::getLog(); $log->setLogLevel($_S->swiftLog); Swift_Errors::expect($e, "Swift_Exception"); if ($_S->smtpServer) { require_once GORUM_DIR . "/SwiftMailer/Swift/Connection/SMTP.php"; if ($_S->fallBackNative) { require_once GORUM_DIR . "/SwiftMailer/Swift/Connection/NativeMail.php"; require_once GORUM_DIR . "/SwiftMailer/Swift/Connection/Multi.php"; $connections = array(); $conn =& new Swift_Connection_SMTP($_S->smtpServer); $conn->setUsername($_S->smtpUser); $conn->setPassword($_S->smtpPass); $conn->setPort($_S->smtpPort); $conn->setPort($_S->smtpPort); $conn->setEncryption($_S->smtpSecure); if ($e !== null) { $error = $e->getMessage(); return; } $connections[] =& $conn; // Fall back to native mail: $connections[] =& new Swift_Connection_NativeMail(); if ($e !== null) { $error = $e->getMessage(); return; } $swift =& new Swift(new Swift_Connection_Multi($connections)); } else { $connection =& new Swift_Connection_SMTP($_S->smtpServer); $connection->setUsername($_S->smtpUser); $connection->setPassword($_S->smtpPass); $connection->setPort($_S->smtpPort); $connection->setEncryption($_S->smtpSecure); if ($e !== null) { $error = $e->getMessage(); return; } $swift =& new Swift($connection); } } else { require_once GORUM_DIR . "/SwiftMailer/Swift/Connection/NativeMail.php"; $swift =& new Swift(new Swift_Connection_NativeMail()); } error_reporting($errorReportingLevel); if ($e !== null) { $error = $e->getMessage(); return; } else { $error = ""; Swift_Errors::clear("Swift_Exception"); } $subject = str_replace(array("<", ">"), array("<", ">"), $subject); $charset = "utf-8"; $message =& new Swift_Message($subject); $message->setCharset($charset); $part1 =& new Swift_Message_Part($body, "text/html"); $part1->setCharset($charset); $message->attach($part1); $part2 =& new Swift_Message_Part(strip_tags($body)); $part2->setCharset($charset); $message->attach($part2); if ($repto) { $message->setReplyTo(new Swift_Address($repto, $reptoName)); } error_reporting(0); Swift_Errors::expect($e, "Swift_Exception"); $recipients = new Swift_RecipientList(); $recipients->addTo($to); if ($this->cc) { $recipients->addCc($this->cc); } $swift->send($message, $recipients, new Swift_Address($from, $fromName)); if ($e !== null) { $error = $e->getMessage(); } else { $error = ""; Swift_Errors::clear("Swift_Exception"); } $swift->disconnect(); error_reporting($errorReportingLevel); }
static function sendTicketMessage($properties = array()) { $settings = CerberusSettings::getInstance(); $helpdesk_senders = CerberusApplication::getHelpdeskSenders(); @($from_addy = $settings->get(CerberusSettings::DEFAULT_REPLY_FROM, $_SERVER['SERVER_ADMIN'])); @($from_personal = $settings->get(CerberusSettings::DEFAULT_REPLY_PERSONAL, '')); // [TODO] If we still don't have a $from_addy we need a graceful failure. /* * [TODO] Move these into constants? 'message_id' -----'ticket_id' 'subject' 'to' 'cc' 'bcc' 'content' 'files' 'closed' 'ticket_reopen' 'unlock_date' 'bucket_id' 'agent_id', 'is_autoreply', 'dont_save_copy' */ $mail_succeeded = true; try { // objects $mail_service = DevblocksPlatform::getMailService(); $mailer = $mail_service->getMailer(CerberusMail::getMailerDefaults()); $mail = $mail_service->createMessage(); // properties @($reply_message_id = $properties['message_id']); @($content =& $properties['content']); @($files = $properties['files']); @($forward_files = $properties['forward_files']); @($worker_id = $properties['agent_id']); @($subject = $properties['subject']); $message = DAO_Ticket::getMessage($reply_message_id); $message_headers = DAO_MessageHeader::getAll($reply_message_id); $ticket_id = $message->ticket_id; $ticket = DAO_Ticket::getTicket($ticket_id); // [TODO] Check that message|ticket isn't NULL // If this ticket isn't spam trained and our outgoing message isn't an autoreply if ($ticket->spam_training == CerberusTicketSpamTraining::BLANK && (!isset($properties['is_autoreply']) || !$properties['is_autoreply'])) { CerberusBayes::markTicketAsNotSpam($ticket_id); } // Allow teams to override the default from/personal @($group_reply = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_FROM, '')); @($group_personal = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_PERSONAL, '')); @($group_personal_with_worker = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_REPLY_PERSONAL_WITH_WORKER, 0)); if (!empty($group_reply)) { $from_addy = $group_reply; } if (!empty($group_personal)) { $from_personal = $group_personal; } // Prefix the worker name on the personal line? if (!empty($group_personal_with_worker) && null != ($reply_worker = DAO_Worker::getAgent($worker_id))) { $from_personal = $reply_worker->getName() . (!empty($from_personal) ? ', ' . $from_personal : ""); } $sendFrom = new Swift_Address($from_addy, $from_personal); // Headers $mail->setFrom($sendFrom); $mail->generateId(); $mail->headers->set('X-Mailer', 'Cerberus Helpdesk (Build ' . APP_BUILD . ')'); // Subject if (empty($subject)) { $subject = $ticket->subject; } if (!empty($properties['to'])) { // forward $mail->setSubject($subject); } else { // reply @($group_has_subject = intval(DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_SUBJECT_HAS_MASK, 0))); @($group_subject_prefix = DAO_GroupSettings::get($ticket->team_id, DAO_GroupSettings::SETTING_SUBJECT_PREFIX, '')); $prefix = sprintf("[%s#%s] ", !empty($group_subject_prefix) ? $group_subject_prefix . ' ' : '', $ticket->mask); $mail->setSubject(sprintf('Re: %s%s', $group_has_subject ? $prefix : '', $subject)); } $sendTo = new Swift_RecipientList(); // References if (!empty($message) && false !== @($in_reply_to = $message_headers['message-id'])) { $mail->headers->set('References', $in_reply_to); $mail->headers->set('In-Reply-To', $in_reply_to); } // Auto-reply handling (RFC-3834 compliant) if (isset($properties['is_autoreply']) && $properties['is_autoreply']) { $mail->headers->set('Auto-Submitted', 'auto-replied'); if (null == ($first_address = DAO_Address::get($ticket->first_wrote_address_id))) { return; } // Don't send e-mail to ourselves if (isset($helpdesk_senders[$first_address->email])) { return; } // Make sure we haven't mailed this address an autoreply within 5 minutes if ($first_address->last_autoreply > 0 && $first_address->last_autoreply > time() - 300) { return; } $first_email = strtolower($first_address->email); $first_split = explode('@', $first_email); if (!is_array($first_split) || count($first_split) != 2) { return; } // If return-path is blank if (isset($message_headers['return-path']) && $message_headers['return-path'] == '<>') { return; } // Ignore bounces if ($first_split[0] == "postmaster" || $first_split[0] == "mailer-daemon") { return; } // Ignore autoresponses to autoresponses if (isset($message_headers['auto-submitted']) && $message_headers['auto-submitted'] != 'no') { return; } if (isset($message_headers['precedence']) && ($message_headers['precedence'] == 'list' || $message_headers['precedence'] == 'junk' || ($message_headers['precedence'] = 'bulk'))) { return; } // Set the auto-reply date for this address to right now DAO_Address::update($ticket->first_wrote_address_id, array(DAO_Address::LAST_AUTOREPLY => time())); // Auto-reply just to the initial requester $sendTo->addTo($first_address->email); $mail->setTo($first_address->email); // Not an auto-reply } else { // Forwards if (!empty($properties['to'])) { $to = array(); $aTo = DevblocksPlatform::parseCsvString(str_replace(';', ',', $properties['to'])); foreach ($aTo as $addy) { $to[] = new Swift_Address($addy); $sendTo->addTo($addy); } if (!empty($to)) { $mail->setTo($to); } // Replies } else { // Recipients $to = array(); $requesters = DAO_Ticket::getRequestersByTicket($ticket_id); if (is_array($requesters)) { foreach ($requesters as $requester) { /* @var $requester Model_Address */ $to[] = new Swift_Address($requester->email); $sendTo->addTo($requester->email); } } $mail->setTo($to); } // Ccs if (!empty($properties['cc'])) { $ccs = array(); $aCc = DevblocksPlatform::parseCsvString(str_replace(';', ',', $properties['cc'])); foreach ($aCc as $addy) { $sendTo->addCc($addy); $ccs[] = new Swift_Address($addy); } if (!empty($ccs)) { $mail->setCc($ccs); } } // Bccs if (!empty($properties['bcc'])) { $aBcc = DevblocksPlatform::parseCsvString(str_replace(';', ',', $properties['bcc'])); foreach ($aBcc as $addy) { $sendTo->addBcc($addy); } } } /* * [IMPORTANT -- Yes, this is simply a line in the sand.] * You're welcome to modify the code to meet your needs, but please respect * our licensing. Buy a legitimate copy to help support the project! * http://www.cerberusweb.com/ */ $license = CerberusLicense::getInstance(); if (empty($license) || @empty($license['serial'])) { $content .= base64_decode("DQoNCi0tLQ0KQ29tYmF0IHNwYW0gYW5kIGltcHJvdmUgcmVzc" . "G9uc2UgdGltZXMgd2l0aCBDZXJiZXJ1cyBIZWxwZGVzayA0LjAhDQpodHRwOi8vd3d3LmNlc" . "mJlcnVzd2ViLmNvbS8NCg"); } // Body $mail->attach(new Swift_Message_Part($content, 'text/plain', 'base64', LANG_CHARSET_CODE)); // Mime Attachments if (is_array($files) && !empty($files)) { foreach ($files['tmp_name'] as $idx => $file) { if (empty($file) || empty($files['name'][$idx])) { continue; } $mail->attach(new Swift_Message_Attachment(new Swift_File($file), $files['name'][$idx], $files['type'][$idx])); } } // Forward Attachments if (!empty($forward_files) && is_array($forward_files)) { $attachments_path = APP_STORAGE_PATH . '/attachments/'; foreach ($forward_files as $file_id) { $attachment = DAO_Attachment::get($file_id); $attachment_path = $attachments_path . $attachment->filepath; $mail->attach(new Swift_Message_Attachment(new Swift_File($attachment_path), $attachment->display_name, $attachment->mime_type)); } } if (!DEMO_MODE) { if (!$mailer->send($mail, $sendTo, $sendFrom)) { $mail_succeeded = false; throw new Exception('Mail not sent.'); } } } catch (Exception $e) { // tag failure, so we can add a note to the message later $mail_succeeded = false; } // Handle post-mail actions $change_fields = array(); $fromAddressInst = CerberusApplication::hashLookupAddress($from_addy, true); $fromAddressId = $fromAddressInst->id; if ((!isset($properties['dont_keep_copy']) || !$properties['dont_keep_copy']) && (!isset($properties['is_autoreply']) || !$properties['is_autoreply'])) { $change_fields[DAO_Ticket::LAST_WROTE_ID] = $fromAddressId; $change_fields[DAO_Ticket::UPDATED_DATE] = time(); if (!empty($worker_id)) { $change_fields[DAO_Ticket::LAST_WORKER_ID] = $worker_id; $change_fields[DAO_Ticket::LAST_ACTION_CODE] = CerberusTicketActionCode::TICKET_WORKER_REPLY; } // Only change the subject if not forwarding if (!empty($subject) && empty($properties['to'])) { $change_fields[DAO_Ticket::SUBJECT] = $subject; } $fields = array(DAO_Message::TICKET_ID => $ticket_id, DAO_Message::CREATED_DATE => time(), DAO_Message::ADDRESS_ID => $fromAddressId, DAO_Message::IS_OUTGOING => 1, DAO_Message::WORKER_ID => !empty($worker_id) ? $worker_id : 0); $message_id = DAO_Message::create($fields); // Content DAO_MessageContent::create($message_id, $content); // Headers if (!empty($mail->headers) && method_exists($mail->headers, 'getList')) { foreach ($mail->headers->getList() as $hdr => $v) { if (null != ($hdr_val = $mail->headers->getEncoded($hdr))) { if (!empty($hdr_val)) { DAO_MessageHeader::create($message_id, $ticket_id, $hdr, CerberusParser::fixQuotePrintableString($hdr_val)); } } } } if (is_array($files) && !empty($files)) { $attachment_path = APP_STORAGE_PATH . '/attachments/'; reset($files); foreach ($files['tmp_name'] as $idx => $file) { if (empty($file) || empty($files['name'][$idx]) || !file_exists($file)) { continue; } $fields = array(DAO_Attachment::MESSAGE_ID => $message_id, DAO_Attachment::DISPLAY_NAME => $files['name'][$idx], DAO_Attachment::MIME_TYPE => $files['type'][$idx], DAO_Attachment::FILE_SIZE => filesize($file)); $file_id = DAO_Attachment::create($fields); $attachment_bucket = sprintf("%03d/", mt_rand(1, 100)); $attachment_file = $file_id; if (!file_exists($attachment_path . $attachment_bucket)) { mkdir($attachment_path . $attachment_bucket, 0775, true); } if (!is_writeable($attachment_path . $attachment_bucket)) { echo "Can't write to " . $attachment_path . $attachment_bucket . "<BR>"; } copy($file, $attachment_path . $attachment_bucket . $attachment_file); @unlink($file); DAO_Attachment::update($file_id, array(DAO_Attachment::FILEPATH => $attachment_bucket . $attachment_file)); } } // add note to message if email failed if ($mail_succeeded === false) { $fields = array(DAO_MessageNote::MESSAGE_ID => $message_id, DAO_MessageNote::CREATED => time(), DAO_MessageNote::WORKER_ID => 0, DAO_MessageNote::CONTENT => 'Exception thrown while sending email: ' . $e->getMessage(), DAO_MessageNote::TYPE => Model_MessageNote::TYPE_ERROR); DAO_MessageNote::create($fields); } } // Post-Reply Change Properties if (isset($properties['closed'])) { switch ($properties['closed']) { case 0: // open $change_fields[DAO_Ticket::IS_WAITING] = 0; $change_fields[DAO_Ticket::IS_CLOSED] = 0; $change_fields[DAO_Ticket::IS_DELETED] = 0; $change_fields[DAO_Ticket::DUE_DATE] = 0; break; case 1: // closed $change_fields[DAO_Ticket::IS_WAITING] = 0; $change_fields[DAO_Ticket::IS_CLOSED] = 1; $change_fields[DAO_Ticket::IS_DELETED] = 0; if (isset($properties['ticket_reopen'])) { @($time = intval(strtotime($properties['ticket_reopen']))); $change_fields[DAO_Ticket::DUE_DATE] = $time; } break; case 2: // waiting $change_fields[DAO_Ticket::IS_WAITING] = 1; $change_fields[DAO_Ticket::IS_CLOSED] = 0; $change_fields[DAO_Ticket::IS_DELETED] = 0; if (isset($properties['ticket_reopen'])) { @($time = intval(strtotime($properties['ticket_reopen']))); $change_fields[DAO_Ticket::DUE_DATE] = $time; } break; } } // Who should handle the followup? if (isset($properties['next_worker_id'])) { $change_fields[DAO_Ticket::NEXT_WORKER_ID] = $properties['next_worker_id']; } // Allow anybody to reply after if (isset($properties['unlock_date']) && !empty($properties['unlock_date'])) { $unlock = strtotime($properties['unlock_date']); if (intval($unlock) > 0) { $change_fields[DAO_Ticket::UNLOCK_DATE] = $unlock; } } // Move if (!empty($properties['bucket_id'])) { // [TODO] Use API to move, or fire event // [TODO] Ensure team/bucket exist list($team_id, $bucket_id) = CerberusApplication::translateTeamCategoryCode($properties['bucket_id']); $change_fields[DAO_Ticket::TEAM_ID] = $team_id; $change_fields[DAO_Ticket::CATEGORY_ID] = $bucket_id; } if (!empty($ticket_id) && !empty($change_fields)) { DAO_Ticket::updateTicket($ticket_id, $change_fields); } // Outbound Reply Event (not automated reply, etc.) if (!empty($worker_id)) { $eventMgr = DevblocksPlatform::getEventService(); $eventMgr->trigger(new Model_DevblocksEvent('ticket.reply.outbound', array('ticket_id' => $ticket_id, 'worker_id' => $worker_id))); } }
public function testFailedRecipientsAreReturned() { $conn = new FullMockConnection(); $conn->setReturnValueAt(0, "read", "220 xxx ESMTP"); $conn->setReturnValueAt(1, "read", "250-Hello xxx\r\n250 HELP"); $conn->setReturnValueAt(2, "read", "250 Ok"); $conn->setReturnValueAt(3, "read", "550 Denied"); $conn->setReturnValueAt(4, "read", "250 ok"); $conn->setReturnValueAt(5, "read", "550 Denied"); $conn->setReturnValueAt(6, "read", "354 Go ahead"); $conn->setReturnValueAt(7, "read", "250 ok"); $log = Swift_LogContainer::getLog(); $swift = new Swift($conn, "abc", Swift::ENABLE_LOGGING); $message = new Swift_Message("My Subject", "my body"); $recipients = new Swift_RecipientList(); $recipients->addTo("*****@*****.**", "XXX YYY"); $recipients->addTo("*****@*****.**"); $recipients->addCc("*****@*****.**"); $this->assertEqual(1, $swift->send($message, $recipients, new Swift_Address("*****@*****.**", "Foo Bar"))); $this->assertEqual(array("*****@*****.**", "*****@*****.**"), $log->getFailedRecipients()); }