/** * Send an email to a number of recipients * Returns the number of successful recipients, or FALSE on failure * @param mixed The recipients to send to. One of string, array, 2-dimensional array or Swift_Address * @param mixed The address to send from. string or Swift_Address * @param string The message subject * @param string The message body, optional * @return int */ public function send($recipients, $from, $subject, $body = null) { $this->addTo($recipients); $sender = false; if (is_string($from)) { $sender = $this->stringToAddress($from); } elseif ($from instanceof Swift_Address) { $sender = $from; } if (!$sender) { return false; } $this->message->setSubject($subject); if ($body) { $this->message->setBody($body); } try { if (!$this->exactCopy && !$this->recipients->getCc() && !$this->recipients->getBcc()) { $sent = $this->swift->batchSend($this->message, $this->recipients, $sender); } else { $sent = $this->swift->send($this->message, $this->recipients, $sender); } if ($this->autoFlush) { $this->flush(); } return $sent; } catch (Swift_ConnectionException $e) { $this->setError("Sending failed:<br />" . $e->getMessage()); return false; } }
/** * Send an email to a number of recipients * Returns the number of successful recipients, or FALSE on failure * @param mixed The recipients to send to. One of string, array, 2-dimensional array or Swift_Address * @param mixed The address to send from. string or Swift_Address * @param string The message subject * @param string The message body, optional * @return int */ function send($recipients, $from, $subject, $body = null) { $this->addTo($recipients); $sender = false; if (is_string($from)) { $sender = $this->stringToAddress($from); } elseif (is_a($from, "Swift_Address")) { $sender =& $from; } if (!$sender) { return false; } $this->message->setSubject($subject); if ($body) { $this->message->setBody($body); } $sent = 0; Swift_Errors::expect($e, "Swift_ConnectionException"); if (!$this->exactCopy && !$this->recipients->getCc() && !$this->recipients->getBcc()) { $sent = $this->swift->batchSend($this->message, $this->recipients, $sender); } else { $sent = $this->swift->send($this->message, $this->recipients, $sender); } if (!$e) { Swift_Errors::clear("Swift_ConnectionException"); if ($this->autoFlush) { $this->flush(); } return $sent; } $this->setError("Sending failed:<br />" . $e->getMessage()); return false; }
/** * Sends notifications *now* * @param mixed $to string or array...the type of address (email, task ID, user ID) is specified below * @param integer $to_type type of $to address * @param integer $type type of notification * @param array $data additional info needed for notification * @access public * @return bool */ function send_now($to, $to_type, $type, $data = array()) { global $db, $fs, $proj; $emails = array(); $jids = array(); $result = true; if (defined('FS_NO_MAIL')) { return true; } switch ($to_type) { case ADDRESS_DONE: // from send_stored() list($emails, $jids) = $to; $data = unserialize($data['message_data']); $subject = $data['subject']; $body = $data['body']; break; case ADDRESS_EMAIL: // this happens on email confirmation, when no user exists $emails = is_array($to) ? $to : array($to); break; case ADDRESS_USER: // list of user IDs list($emails, $jids) = Notifications::user_to_address($to, $type); break; case ADDRESS_TASK: // now we need everyone on the notification list and the assignees list($emails, $jids) = Notifications::task_notifications($to, $type, ADDRESS_EMAIL); $data['task_id'] = $to; break; } if (isset($data['task_id'])) { $data['task'] = Flyspray::getTaskDetails($data['task_id']); // we have project specific options $pid = $db->x->GetOne('SELECT project_id FROM {tasks} WHERE task_id = ?', null, $data['task_id']); $data['project'] = new Project($pid); } if ($to_type != ADDRESS_DONE) { list($subject, $body) = Notifications::generate_message($type, $data); } if (isset($data['task_id'])) { // Now, we add the project contact addresses, // but only if the task is public $data['task'] = Flyspray::getTaskDetails($data['task_id']); if ($data['task']['mark_private'] != '1' && in_array($type, explode(' ', $data['project']->prefs['notify_types']))) { $proj_emails = preg_split('/[\\s,;]+/', $proj->prefs['notify_email'], -1, PREG_SPLIT_NO_EMPTY); $proj_jids = preg_split('/[\\s,;]+/', $proj->prefs['notify_jabber'], -1, PREG_SPLIT_NO_EMPTY); $emails = array_merge($proj_emails, $emails); if ($fs->prefs['global_email']) { $emails[] = $fs->prefs['global_email']; } if ($fs->prefs['global_jabber']) { $jids[] = $fs->prefs['global_jabber']; } $jids = array_merge($proj_jids, $emails); } } // Now we start sending if (count($emails)) { Swift_ClassLoader::load('Swift_Connection_Multi'); Swift_ClassLoader::load('Swift_Connection_SMTP'); $pool = new Swift_Connection_Multi(); // first choose method if ($fs->prefs['smtp_server']) { $split = explode(':', $fs->prefs['smtp_server']); $port = null; if (count($split) == 2) { $fs->prefs['smtp_server'] = $split[0]; $port = $split[1]; } // connection... SSL, TLS or none if ($fs->prefs['email_ssl']) { $smtp = new Swift_Connection_SMTP($fs->prefs['smtp_server'], $port ? $port : SWIFT_SMTP_PORT_SECURE, SWIFT_SMTP_ENC_SSL); } else { if ($fs->prefs['email_tls']) { $smtp = new Swift_Connection_SMTP($fs->prefs['smtp_server'], $port ? $port : SWIFT_SMTP_PORT_SECURE, SWIFT_SMTP_ENC_TLS); } else { $smtp = new Swift_Connection_SMTP($fs->prefs['smtp_server'], $port); } } if ($fs->prefs['smtp_user']) { $smtp->setUsername($fs->prefs['smtp_user']); $smtp->setPassword($fs->prefs['smtp_pass']); } if (defined('FS_SMTP_TIMEOUT')) { $smtp->setTimeout(FS_SMTP_TIMEOUT); } $pool->addConnection($smtp); } else { Swift_ClassLoader::load('Swift_Connection_NativeMail'); // a connection to localhost smtp server as fallback, discarded if there is no such thing available. $pool->addConnection(new Swift_Connection_SMTP()); $pool->addConnection(new Swift_Connection_NativeMail()); } $swift = new Swift($pool); if (isset($data['task_id'])) { $swift->attachPlugin(new NotificationsThread($data['task_id'], $emails, $db), 'MessageThread'); } if (defined('FS_MAIL_DEBUG')) { $swift->log->enable(); Swift_ClassLoader::load('Swift_Plugin_VerboseSending'); $view = new Swift_Plugin_VerboseSending_DefaultView(); $swift->attachPlugin(new Swift_Plugin_VerboseSending($view), "verbose"); } $message = new Swift_Message($subject, $body); // check for reply-to if (isset($data['project']) && $data['project']->prefs['notify_reply']) { $message->setReplyTo($data['project']->prefs['notify_reply']); } if (isset($data['project']) && isset($data['project']->prefs['bounce_address'])) { $message->setReturnPath($data['project']->prefs['bounce_address']); } $message->headers->setCharset('utf-8'); $message->headers->set('Precedence', 'list'); $message->headers->set('X-Mailer', 'Flyspray'); // Add custom headers, possibly if (isset($data['headers'])) { $headers = array_map('trim', explode("\n", $data['headers'])); if ($headers = array_filter($headers)) { foreach ($headers as $header) { list($name, $value) = explode(':', $header); $message->headers->set(sprintf('X-Flyspray-%s', $name), $value); } } } $recipients = new Swift_RecipientList(); $recipients->addTo($emails); // && $result purpose: if this has been set to false before, it should never become true again // to indicate an error $result = $swift->batchSend($message, $recipients, $fs->prefs['admin_email']) === count($emails) && $result; if (isset($data['task_id'])) { $plugin =& $swift->getPlugin('MessageThread'); if (count($plugin->thread_info)) { $stmt = $db->x->autoPrepare('{notification_threads}', array('task_id', 'recipient_id', 'message_id')); $db->x->executeMultiple($stmt, $plugin->thread_info); $stmt->free(); } } $swift->disconnect(); } if (count($jids)) { $jids = array_unique($jids); if (!$fs->prefs['jabber_username'] || !$fs->prefs['jabber_password']) { return $result; } // nothing that can't be guessed correctly ^^ if (!$fs->prefs['jabber_port']) { $fs->prefs['jabber_port'] = 5222; } require_once 'class.jabber2.php'; $jabber = new Jabber($fs->prefs['jabber_username'], $fs->prefs['jabber_password'], $fs->prefs['jabber_security'], $fs->prefs['jabber_port'], $fs->prefs['jabber_server']); $jabber->SetResource('flyspray'); $jabber->login(); foreach ($jids as $jid) { $result = $jabber->send_message($jid, $body, $subject, 'normal') && $result; } } return $result; }
/** * Send email to list of recipients * * $to is a list of users who need to receive email notifications. If this * function gets list of email addresses default language will be used. If * we get User instances we'll use language set as prefered on their profile * page * * $to can also be a single user or email address * * $tpl is a script in format module/name. If / is not present activeCollab * will assume that template is in system module * * Context is object that this email is primary related to * * $attachments is array of attachments that are structured like this * path -> path to file * name -> name which will be displayed in email (if ommited original filename will be used) * mime_type -> file mime type (if ommited system will determine mime type automatically) * here is sample of one $attachments array * $attachments = array( * array('path' => '/work/picture3.png', 'name' => 'simple_file_name.png', 'mime_type' => 'image/png') * ); * * @param array $to * @param string $tpl * @param array $replacements * @param mixed $context * @param array $attachments * @return boolean */ function send($to, $tpl, $replacements = null, $context = null, $attachments = null) { static $mark_as_bulk = null, $empty_return_path = null; if (isset($this) && instance_of($this, 'ApplicationMailer')) { if (!$this->connected) { $this->connect(); } // if if (!is_foreachable($to)) { if (instance_of($to, 'User') || is_valid_email($to)) { $to = array($to); } else { return true; // no recipients } // if } // if if (strpos($tpl, '/') === false) { $template_module = SYSTEM_MODULE; } else { list($template_module, $template_name) = explode('/', $tpl); } // if $template = EmailTemplates::findById(array('module' => $template_module, 'name' => $template_name)); if (!instance_of($template, 'EmailTemplate')) { return false; } // if $owner_company = get_owner_company(); if (is_array($replacements)) { $replacements['owner_company_name'] = $owner_company->getName(); } else { $replacements = array('owner_company_name' => $owner_company->getName()); } // if // Array of messages and recipients organized by language $to_send = array(); // Set default locale (built in one) $default_locale = BUILT_IN_LOCALE; // Do we have a default language set $default_language_id = ConfigOptions::getValue('language'); if ($default_language_id) { $default_language = Languages::findById($default_language_id); if (instance_of($default_language, 'Language') && !$default_language->isBuiltIn()) { $default_locale = $default_language->getLocale(); } // if } // if // Cache of loaded languages $languages = array(); // Get from email and from name $from_email = ConfigOptions::getValue('notifications_from_email'); $from_name = ConfigOptions::getValue('notifications_from_name'); if (!is_valid_email($from_email)) { $from_email = ADMIN_EMAIL; } // if if (empty($from_name)) { $from_name = $owner_company->getName(); } // if // Now prepare messages foreach ($to as $recipient) { $locale = $default_locale; if (instance_of($recipient, 'User')) { $locale = $recipient->getLocale($default_locale); $recipient_name = $recipient->getDisplayName(); $recipient_email = $recipient->getEmail(); // If same reset name... "name@site.com <*****@*****.**>" can cause // problems with some servers if ($recipient_name == $recipient_email) { $recipient_name = null; } // if } else { $recipient_name = null; $recipient_email = $recipient; } // if $language = isset($languages[$locale]) ? $languages[$locale] : Languages::findByLocale($locale); // We have message prepared, just need to add a recipient //BOF:mod //if(isset($to_send[$locale])) { if (isset($to_send[$locale]) && $tpl != 'resources/new_comment') { //EOF:moid $to_send[$locale]['recipients']->add($recipient_email, $recipient_name); // Need to prepare message and add first recipient } else { //BOF:mod 20110711 ticketid231 if ($tpl == 'resources/new_comment') { $mark_actionrequest_complete = ''; $link = mysql_connect(DB_HOST, DB_USER, DB_PASS); mysql_select_db(DB_NAME); $query = "select a.is_action_request, b.project_id from healingcrystals_assignments_action_request a inner join healingcrystals_project_objects b on a.comment_id=b.id where a.comment_id='" . $replacements['comment_id'] . "' and a.user_id='" . $recipient->getId() . "'"; $result = mysql_query($query); if (mysql_num_rows($result)) { $info = mysql_fetch_assoc($result); if ($info['is_action_request'] == '1') { $mark_actionrequest_complete = '<div style="margin:5px 0 5px 0;"><a href="' . assemble_url('project_comment_action_request_completed', array('project_id' => $info['project_id'], 'comment_id' => $replacements['comment_id'])) . '">Click here to Mark this Action Request Complete</a></div>'; } } mysql_close($link); $replacements['mark_actionrequest_complete'] = $mark_actionrequest_complete; } //EOF:mod 20110711 ticketid231 $subject = $template->getSubject($locale); $body = $template->getBody($locale); foreach ($replacements as $k => $v) { if (is_array($v)) { $v = isset($v[$locale]) ? $v[$locale] : array_shift($v); } // if $subject = str_replace(":{$k}", $v, $subject); if (str_ends_with($k, '_body')) { $body = str_replace(":{$k}", $v, $body); } else { //$body = str_replace(":$k", clean($v), $body); $body = str_replace(":{$k}", $v, $body); } // if } // foreach //BOF:mod //BOF:mod 20111101 /* //EOF:mod 20111101 if ($tpl=='resources/new_comment'){ $add_to_subject = ''; $link = mysql_connect(DB_HOST, DB_USER, DB_PASS); mysql_select_db(DB_NAME); $query = "select a.is_action_request, a.is_fyi, a.priority_actionrequest, b.project_id from healingcrystals_assignments_action_request a inner join healingcrystals_project_objects b on a.comment_id=b.id where a.comment_id='" . $replacements['comment_id'] . "' and a.user_id='" . $recipient->getId() . "'"; $result = mysql_query($query, $link); if (mysql_num_rows($result)){ $info = mysql_fetch_assoc($result); $flag_action_request = $info['is_action_request']; $flag_fyi = $info['is_fyi']; $priority = $info['priority_actionrequest']; if ($flag_action_request=='1'){ switch($priority){ case PRIORITY_LOWEST: $priority_desc = 'Lowest Priority '; break; case PRIORITY_LOW: $priority_desc = 'Low Priority '; break; case PRIORITY_NORMAL: $priority_desc = 'Normal Priority '; break; case PRIORITY_HIGH: $priority_desc = 'High Priority '; break; case PRIORITY_HIGHEST: $priority_desc = 'Highest Priority '; break; default: $priority_desc = ''; } $add_to_subject .= $priority_desc . 'Action Request'; } if ($flag_fyi=='1'){ if (!empty($add_to_subject)){ $add_to_subject .= '/'; } $add_to_subject .= 'FYI'; $body = '<a href="' . assemble_url('project_comment_fyi_read', array('project_id' => $info['project_id'], 'comment_id' => $replacements['comment_id'])) . '">Mark this FYI Notification as Read</a>' . "<br>\n" . $body; } if (!empty($add_to_subject)){ $add_to_subject .= ' - '; } } mysql_close($link); $subject = $add_to_subject . $subject; } //EOF:mod //BOF:mod 20111101 */ //EOF:mod 20111101 //BOF:mod 20111110 #493 if ($tpl == 'resources/new_comment') { $subject .= ' [CID' . $replacements['comment_id'] . ']'; } //EOF:mod 20111110 #493 event_trigger('on_prepare_email', array($tpl, $recipient_email, $context, &$body, &$subject, &$attachments, &$language, $replacements['subscribers_name'])); // if files need to be attached, message will be multipart if (is_foreachable($attachments)) { $message = new Swift_Message($subject); $message->attach(new Swift_Message_Part($body, 'text/html', EMAIL_ENCODING, EMAIL_CHARSET)); foreach ($attachments as $attachment) { $file_path = array_var($attachment, 'path', null); if (file_exists($file_path)) { $message->attach(new Swift_Message_Attachment(new Swift_File($file_path), array_var($attachment, 'name', basename($file_path)), array_var($attachment, 'mime_type', mime_content_type($file_path)))); } } // if } else { $message = new Swift_Message($subject, $body, 'text/html', EMAIL_ENCODING, EMAIL_CHARSET); } // if // Load values... if ($mark_as_bulk === null || $empty_return_path === null) { $mark_as_bulk = (bool) ConfigOptions::getValue('mailing_mark_as_bulk'); $empty_return_path = (bool) ConfigOptions::getValue('mailing_empty_return_path'); } // if // Custom headers (to prevent auto responders) if ($mark_as_bulk) { $message->headers->set('Auto-Submitted', 'auto-generated'); $message->headers->set('Precedence', 'bulk'); } // if if ($empty_return_path) { $message->headers->set('Return-Path', '<>'); } else { $message->headers->set('Return-Path', "<{$from_email}>"); } // if if (!isset($to_send[$locale])) { $to_send[$locale] = array('recipients' => new Swift_RecipientList(), 'message' => $message); } //BOF:mod //BOF:mod 20111101 /* //EOF:mod 20111101 if ($tpl=='resources/new_comment'){ $to_send[$locale]['modified_subject'][$recipient_email] = $subject; } //BOF:mod 20111101 */ //EOF:mod 20111101 //EOF:mod $to_send[$locale]['recipients']->add($recipient_email, $recipient_name); } // if } // foreach if (is_foreachable($to_send)) { foreach ($to_send as $locale => $message_data) { //BOF:mod 20111101 /* //EOF:mod 20111101 $this->swift->batchSend($message_data['message'], $message_data['recipients'], new Swift_Address($from_email, $from_name), $message_data['modified_subject']); //BOF:mod 20111101 */ $this->swift->batchSend($message_data['message'], $message_data['recipients'], new Swift_Address($from_email, $from_name)); //EOF:mod 20111101 } // foreach } // if return true; } else { $instance =& ApplicationMailer::instance(); return $instance->send($to, $tpl, $replacements, $context, $attachments); } // if }
public function testBatchSendingDoesNotCopyAllRecipientsInOnASingleEmail() { //Most of this test has been removed due to complexity working out when the commands when be issued. Sorry. $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", "250 Ok"); $conn->setReturnValueAt(4, "read", "354 Go ahead"); $conn->setReturnValueAt(5, "read", "250 ok"); $conn->setReturnValueAt(6, "read", "250 Ok"); $conn->setReturnValueAt(7, "read", "250 Ok"); $conn->setReturnValueAt(8, "read", "354 Go ahead"); $conn->setReturnValueAt(9, "read", "250 ok"); $message = new Swift_Message("My Subject", "my body"); $message->setEncoding("8bit"); $swift = new Swift($conn, "abc", Swift::ENABLE_LOGGING); $recipients = new Swift_RecipientList(); $recipients->addTo("*****@*****.**", "XXX YYY"); $recipients->addTo("*****@*****.**"); $this->assertEqual(2, $swift->batchSend($message, $recipients, new Swift_Address("*****@*****.**", "Foo Bar"))); }