676 lines
26 KiB
PHP
676 lines
26 KiB
PHP
<?php
|
||
/*********************************************************************
|
||
class.mailer.php
|
||
|
||
osTicket/Mail/Mailer
|
||
|
||
Wrapper for sending emails via SMTP / SendMail
|
||
|
||
Peter Rotich <peter@osticket.com>
|
||
Copyright (c) osTicket
|
||
http://www.osticket.com
|
||
|
||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||
See LICENSE.TXT for details.
|
||
|
||
vim: expandtab sw=4 ts=4 sts=4:
|
||
**********************************************************************/
|
||
namespace osTicket\Mail;
|
||
|
||
class Mailer {
|
||
private $from = null;
|
||
var $email = null;
|
||
var $smtpAccounts = [];
|
||
|
||
var $ht = array();
|
||
var $attachments = array();
|
||
var $options = array();
|
||
var $eol="\n";
|
||
|
||
function __construct(\Email $email=null, array $options=array()) {
|
||
global $cfg;
|
||
|
||
// Get all possible outgoing emails accounts (SMTP) to try
|
||
if (($email instanceof \Email)
|
||
&& ($smtp=$email->getSmtpAccount(false))
|
||
&& $smtp->isActive()) {
|
||
$this->smtpAccounts[$smtp->getId()] = $smtp;
|
||
}
|
||
if ($cfg) {
|
||
// Get Default MTA (SMTP)
|
||
if (($smtp=$cfg->getDefaultMTA()) && $smtp->isActive()) {
|
||
$this->smtpAccounts[$smtp->getId()] = $smtp;
|
||
// If email is not set then use Default MTA
|
||
if (!$email)
|
||
$email = $smtp->getEmail();
|
||
} elseif (!$email && $cfg && ($email=$cfg->getDefaultEmail())) {
|
||
// as last resort we will send via Default Email
|
||
if (($smtp=$email->getSmtpAccount(false)) && $smtp->isActive())
|
||
$this->smtpAccounts[$smtp->getId()] = $smtp;
|
||
}
|
||
}
|
||
|
||
$this->email = $email;
|
||
$this->attachments = array();
|
||
$this->options = $options;
|
||
if (isset($this->options['eol']))
|
||
$this->eol = $this->options['eol'];
|
||
elseif (defined('MAIL_EOL') && is_string(MAIL_EOL))
|
||
$this->eol = MAIL_EOL;
|
||
}
|
||
|
||
function getEOL() {
|
||
return $this->eol;
|
||
}
|
||
|
||
function getSmtpAccounts() {
|
||
return $this->smtpAccounts;
|
||
}
|
||
|
||
function getEmail() {
|
||
return $this->email;
|
||
}
|
||
|
||
/* FROM Address */
|
||
function setFromAddress($from=null, $name=null) {
|
||
if ($from instanceof \EmailAddress)
|
||
$this->from = $from;
|
||
elseif (\Validator::is_email($from)) {
|
||
$this->from = new \EmailAddress(
|
||
str_contains($from, '<') ? $from : sprintf('"%s" <%s>', $name ?: '', $from));
|
||
} elseif (is_string($from))
|
||
$this->from = new \EmailAddress($from);
|
||
elseif (($email=$this->getEmail())) {
|
||
// we're assuming from was null or unexpected monster
|
||
$address = sprintf('"%s" <%s>',
|
||
$name ?: $email->getName(),
|
||
$email->getEmail());
|
||
$this->from = new \EmailAddress($address);
|
||
}
|
||
}
|
||
|
||
function getFromAddress($options=array()) {
|
||
if (!isset($this->from))
|
||
$this->setFromAddress(null, $options['from_name'] ?: null);
|
||
|
||
return $this->from;
|
||
}
|
||
|
||
function getFromName() {
|
||
return $this->getFromAddress()->getName();
|
||
}
|
||
|
||
function getFromEmail() {
|
||
return $this->getFromAddress()->getEmail();
|
||
}
|
||
|
||
/* attachments */
|
||
function getAttachments() {
|
||
return $this->attachments;
|
||
}
|
||
|
||
function addAttachment(\Attachment $attachment) {
|
||
$this->attachments[$attachment->getFile()->getUId()] = $attachment;
|
||
}
|
||
|
||
function addAttachmentFile(\AttachmentFile $file) {
|
||
$this->attachments[$file->getUId()] = $file;
|
||
}
|
||
|
||
function addFileObject(\FileObject $file) {
|
||
$this->attachments[$file->getUId()] = $file;
|
||
}
|
||
|
||
function addAttachments($attachments) {
|
||
foreach ($attachments as $a) {
|
||
if ($a instanceof \Attachment)
|
||
$this->addAttachment($a);
|
||
elseif ($a instanceof \AttachmentFile)
|
||
$this->addAttachmentFile($a);
|
||
elseif ($a instanceof \FileObject)
|
||
$this->addFileObject($a);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* lookup Attached File by Key
|
||
*/
|
||
function getFile(String $key) {
|
||
foreach ($this->getAttachments() as $uid => $F) {
|
||
if ($F instanceof \Attachment)
|
||
$F = $F->getFile();
|
||
if (strcasecmp($F->getKey(), $key) === 0)
|
||
return $F;
|
||
}
|
||
return \AttachmentFile::lookup($key);
|
||
}
|
||
|
||
/**
|
||
* getMessageId
|
||
*
|
||
* Generates a unique message ID for an outbound message. Optionally,
|
||
* the recipient can be used to create a tag for the message ID where
|
||
* the user-id and thread-entry-id are encoded in the message-id so
|
||
* the message can be threaded if it is replied to without any other
|
||
* indicator of the thread to which it belongs. This tag is signed with
|
||
* the secret-salt of the installation to guard against false positives.
|
||
*
|
||
* Parameters:
|
||
* $recipient - (EmailContact|null) recipient of the message. The ID of
|
||
* the recipient is placed in the message id TAG section so it can
|
||
* be recovered if the email replied to directly by the end user.
|
||
* $options - (array) - options passed to ::send(). If it includes a
|
||
* 'thread' element, the threadId will be recorded in the TAG
|
||
*
|
||
* Returns:
|
||
* (string) - email message id, without leading and trailing <> chars.
|
||
* See the Format below for the structure.
|
||
*
|
||
* Format:
|
||
* VA-B-C, with dash separators and A-C explained below:
|
||
*
|
||
* V: Version code of the generated Message-Id
|
||
* A: Predictable random code — used for loop detection (sysid)
|
||
* B: Random data for unique identifier (rand)
|
||
* C: TAG: Base64(Pack(userid, entryId, threadId, type, Signature)),
|
||
* '=' chars discarded
|
||
* where Signature is:
|
||
* Signed Tag value, last 5 chars from
|
||
* HMAC(sha1, Tag + rand + sysid, SECRET_SALT),
|
||
* where Tag is:
|
||
* pack(userId, entryId, threadId, type)
|
||
*/
|
||
function getMessageId($recipient, $options=array(), $version='B') {
|
||
$tag = '';
|
||
$rand = \Misc::randCode(5,
|
||
// RFC822 specifies the LHS of the addr-spec can have any char
|
||
// except the specials — ()<>@,;:\".[], dash is reserved as the
|
||
// section separator, and + is reserved for historical reasons
|
||
'abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=');
|
||
$sig = $this->getEmail()?$this->getEmail()->getEmail():'@osTicketMailer';
|
||
$sysid = static::getSystemMessageIdCode();
|
||
// Create a tag for the outbound email
|
||
$entry = (isset($options['thread'])
|
||
&& ($options['thread'] instanceof \ThreadEntry))
|
||
? $options['thread'] : false;
|
||
$thread = $entry ? $entry->getThread()
|
||
: (isset($options['thread'])
|
||
&& ($options['thread'] instanceof \Thread)
|
||
? $options['thread'] : false);
|
||
|
||
switch (true) {
|
||
case $recipient instanceof \Staff:
|
||
$utype = 'S';
|
||
break;
|
||
case $recipient instanceof \TicketOwner:
|
||
$utype = 'U';
|
||
break;
|
||
case $recipient instanceof \Collaborator:
|
||
$utype = 'C';
|
||
break;
|
||
case $recipient instanceof \MailingList:
|
||
$utype = 'M';
|
||
break;
|
||
default:
|
||
$utype = ($options['utype'] ?: is_array($recipient)) ? 'M' : '?';
|
||
}
|
||
|
||
|
||
$tag = pack('VVVa',
|
||
$recipient instanceof \EmailContact ? $recipient->getUserId() : 0,
|
||
$entry ? $entry->getId() : 0,
|
||
$thread ? $thread->getId() : 0,
|
||
$utype ?: '?'
|
||
);
|
||
// Sign the tag with the system secret salt
|
||
$tag .= substr(hash_hmac('sha1', $tag.$rand.$sysid, SECRET_SALT, true), -5);
|
||
$tag = str_replace('=','',base64_encode($tag));
|
||
return sprintf('B%s-%s-%s-%s',
|
||
$sysid, $rand, $tag, $sig);
|
||
}
|
||
|
||
/**
|
||
* decodeMessageId
|
||
*
|
||
* Decodes a message-id generated by osTicket using the ::getMessageId()
|
||
* method of this class. This will digest the received message-id token
|
||
* and return an array with some information about it.
|
||
*
|
||
* Parameters:
|
||
* $mid - (string) message-id from an email Message-Id, In-Reply-To, and
|
||
* References header.
|
||
*
|
||
* Returns:
|
||
* (array) of information containing all or some of the following keys
|
||
* 'loopback' - (bool) true or false if the message originated by
|
||
* this osTicket installation.
|
||
* 'version' - (string|FALSE) version code of the message id
|
||
* 'code' - (string) unique but predictable help desk message-id
|
||
* 'id' - (string) random characters serving as the unique id
|
||
* 'entryId' - (int) thread-entry-id from which the message originated
|
||
* 'threadId' - (int) thread-id from which the message originated
|
||
* 'staffId' - (int|null) staff the email was originally sent to
|
||
* 'userId' - (int|null) user the email was originally sent to
|
||
* 'userClass' - (string) class of user the email was sent to
|
||
* 'U' - TicketOwner
|
||
* 'S' - Staff
|
||
* 'C' - Collborator
|
||
* 'M' - Multiple
|
||
* '?' - Something else
|
||
*/
|
||
static function decodeMessageId($mid) {
|
||
// Drop <> tokens
|
||
$mid = trim($mid, '<> ');
|
||
// Drop email domain on rhs
|
||
list($lhs, $sig) = explode('@', $mid, 2);
|
||
// LHS should be tokenized by '-'
|
||
$parts = explode('-', $lhs);
|
||
|
||
$rv = array('loopback' => false, 'version' => false);
|
||
|
||
// There should be at least two tokens if the message was sent by
|
||
// this system. Otherwise, there's nothing to be detected
|
||
if (count($parts) < 2)
|
||
return $rv;
|
||
|
||
$self = get_called_class();
|
||
$decoders = array(
|
||
'A' => function($id, $tag) use ($sig) {
|
||
// Old format was VA-B-C-D@sig, where C was the packed tag and D
|
||
// was blank
|
||
$format = 'Vuid/VentryId/auserClass';
|
||
$chksig = substr(hash_hmac('sha1', $tag.$id, SECRET_SALT), -10);
|
||
if ($tag && $sig == $chksig && ($tag = base64_decode($tag))) {
|
||
// Find user and ticket id
|
||
return unpack($format, $tag);
|
||
}
|
||
return false;
|
||
},
|
||
'B' => function($id, $tag) use ($self) {
|
||
$format = 'Vuid/VentryId/VthreadId/auserClass/a*sig';
|
||
if ($tag && ($tag = base64_decode($tag))) {
|
||
if (!($info = @unpack($format, $tag)) || !isset($info['sig']))
|
||
return false;
|
||
$sysid = $self::getSystemMessageIdCode();
|
||
$shorttag = substr($tag, 0, 13);
|
||
$chksig = substr(hash_hmac('sha1', $shorttag.$id.$sysid,
|
||
SECRET_SALT, true), -5);
|
||
if ($chksig == $info['sig']) {
|
||
return $info;
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
);
|
||
|
||
// Detect the MessageId version, which should be the first char
|
||
$rv['version'] = @$parts[0][0];
|
||
if (!isset($decoders[$rv['version']]))
|
||
// invalid version code
|
||
return null;
|
||
|
||
// Drop the leading version code
|
||
list($rv['code'], $rv['id'], $tag) = $parts;
|
||
$rv['code'] = substr($rv['code'], 1);
|
||
|
||
// Verify tag signature and unpack the tag
|
||
$info = $decoders[$rv['version']]($rv['id'], $tag);
|
||
if ($info === false)
|
||
return $rv;
|
||
|
||
$rv += $info;
|
||
|
||
// Attempt to make the user-id more specific
|
||
$classes = array(
|
||
'S' => 'staffId', 'U' => 'userId', 'C' => 'userId',
|
||
);
|
||
if (isset($classes[$rv['userClass']]))
|
||
$rv[$classes[$rv['userClass']]] = $rv['uid'];
|
||
|
||
// Round-trip detection - the first section is the local
|
||
// system's message-id code
|
||
$rv['loopback'] = (0 === strcmp($rv['code'],
|
||
static::getSystemMessageIdCode()));
|
||
|
||
return $rv;
|
||
}
|
||
|
||
static function getSystemMessageIdCode() {
|
||
return substr(str_replace('+', '=',
|
||
base64_encode(md5('mail'.SECRET_SALT, true))),
|
||
0, 6);
|
||
}
|
||
|
||
function send($recipients, $subject, $body, $options=null) {
|
||
global $ost, $cfg;
|
||
|
||
$messageId = $this->getMessageId($recipients, $options);
|
||
$subject = preg_replace("/(\r\n|\r|\n)/s",'', trim($subject));
|
||
$from = $this->getFromAddress($options);
|
||
|
||
// Create new ostTicket/Mail/Message object
|
||
$message = new Message();
|
||
// Set our custom Message-Id
|
||
$message->setMessageId($messageId);
|
||
// Set From Address
|
||
$message->setFrom($from->getEmail(), $from->getName());
|
||
// Set Subject
|
||
$message->setSubject($subject);
|
||
|
||
// Collect Generic Headers
|
||
$headers = ['X-Mailer' =>'osTicket Mailer'];
|
||
|
||
// Add in the options passed to the constructor
|
||
$options = ($options ?: array()) + $this->options;
|
||
// Message Id Token
|
||
$mid_token = '';
|
||
// Check if the email is threadable
|
||
if (isset($options['thread'])
|
||
&& ($options['thread'] instanceof \ThreadEntry)
|
||
&& ($thread = $options['thread']->getThread())) {
|
||
|
||
// Add email in-reply-to references if not set
|
||
if (!isset($options['inreplyto'])) {
|
||
|
||
$entry = null;
|
||
switch (true) {
|
||
case $recipients instanceof \MailingList:
|
||
$entry = $thread->getLastEmailMessage();
|
||
break;
|
||
case $recipients instanceof \TicketOwner:
|
||
case $recipients instanceof \Collaborator:
|
||
$entry = $thread->getLastEmailMessage(array(
|
||
'user_id' => $recipients->getUserId()));
|
||
break;
|
||
case $recipients instanceof \Staff:
|
||
//XXX: is it necessary ??
|
||
break;
|
||
}
|
||
|
||
if ($entry && ($mid=$entry->getEmailMessageId())) {
|
||
$options['inreplyto'] = $mid;
|
||
$options['references'] = $entry->getEmailReferences();
|
||
}
|
||
}
|
||
|
||
// Embedded message id token
|
||
$mid_token = $messageId;
|
||
// Set Reply-Tag
|
||
if (!isset($options['reply-tag'])) {
|
||
if ($cfg && $cfg->stripQuotedReply())
|
||
$options['reply-tag'] = $cfg->getReplySeparator() . '<br/><br/>';
|
||
else
|
||
$options['reply-tag'] = '';
|
||
} elseif ($options['reply-tag'] === false) {
|
||
$options['reply-tag'] = '';
|
||
}
|
||
}
|
||
|
||
// Return-Path
|
||
if (isset($options['nobounce']) && $options['nobounce'])
|
||
$message->setReturnPath('<>');
|
||
elseif ($this->getEmail() instanceof \Email)
|
||
$message->setReturnPath($this->getEmail()->getEmail());
|
||
|
||
// Bulk.
|
||
if (isset($options['bulk']) && $options['bulk'])
|
||
$headers+= array('Precedence' => 'bulk');
|
||
|
||
// Auto-reply - mark as autoreply and supress all auto-replies
|
||
if (isset($options['autoreply']) && $options['autoreply']) {
|
||
$headers+= array(
|
||
'Precedence' => 'auto_reply',
|
||
'X-Autoreply' => 'yes',
|
||
'X-Auto-Response-Suppress' => 'DR, RN, OOF, AutoReply',
|
||
'Auto-Submitted' => 'auto-replied');
|
||
}
|
||
|
||
// Notice (sort of automated - but we don't want auto-replies back
|
||
if (isset($options['notice']) && $options['notice'])
|
||
$headers+= array(
|
||
'X-Auto-Response-Suppress' => 'OOF, AutoReply',
|
||
'Auto-Submitted' => 'auto-generated');
|
||
// In-Reply-To
|
||
if (isset($options['inreplyto']) && $options['inreplyto'])
|
||
$message->addInReplyTo($options['inreplyto']);
|
||
|
||
// References
|
||
if (isset($options['references']) && $options['references'])
|
||
$message->addReferences($options['references']);
|
||
|
||
// Add Headers
|
||
$message->addHeaders($headers);
|
||
|
||
// Add recipients
|
||
if (!is_array($recipients) && (!$recipients instanceof \MailingList))
|
||
$recipients = array($recipients);
|
||
foreach ($recipients as $recipient) {
|
||
if ($recipient instanceof \ClientSession)
|
||
$recipient = $recipient->getSessionUser();
|
||
try {
|
||
switch (true) {
|
||
case $recipient instanceof \EmailRecipient:
|
||
$email = (string) $recipient->getEmail()->getEmail();
|
||
$name = (string) $recipient->getName();
|
||
switch ($recipient->getType()) {
|
||
case 'to':
|
||
$message->addTo($email, $name);
|
||
break;
|
||
case 'cc':
|
||
$message->addCc($email, $name);
|
||
break;
|
||
case 'bcc':
|
||
$message->addBcc($email, $name);
|
||
break;
|
||
}
|
||
break;
|
||
case $recipient instanceof \TicketOwner:
|
||
case $recipient instanceof \Staff:
|
||
$message->addTo((string) $recipient->getEmail(),
|
||
(string) $recipient->getName());
|
||
break;
|
||
case $recipient instanceof \Collaborator:
|
||
$message->addCc((string) $recipient->getEmail(),
|
||
(string) $recipient->getName());
|
||
break;
|
||
case $recipient instanceof \EmailAddress:
|
||
$message->addTo((string) $recipient->getEmail(),
|
||
(string) $recipient->getName());
|
||
break;
|
||
default:
|
||
// Assuming email address.
|
||
if (is_string($recipient))
|
||
$message->addTo($recipient);
|
||
}
|
||
} catch(\Exception $ex) {
|
||
$this->logWarning(sprintf("%s1\$s: %2\$s\n\n%3\$s\n",
|
||
_S("Unable to add email recipient"),
|
||
($recipient instanceof EmailContact)
|
||
? $recipient->getEmailAddress()
|
||
: (string) $recipient,
|
||
$ex->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
// Add in extra attachments, if any from template variables
|
||
if ($body instanceof \TextWithExtras
|
||
&& ($attachments = $body->getAttachments())) {
|
||
foreach ($attachments as $a) {
|
||
$message->addAttachment($a->getFile());
|
||
}
|
||
}
|
||
|
||
// If the message is not explicitly declared to be a text message,
|
||
// then assume that it needs html processing to create a valid text
|
||
// body
|
||
$isHtml = true;
|
||
if (!(isset($options['text']) && $options['text'])) {
|
||
// Embed the data-mid in such a way that it should be included
|
||
// in a response
|
||
if ($options['reply-tag'] || $mid_token) {
|
||
$body = sprintf('<div style="display:none"
|
||
class="mid-%s">%s</div>%s',
|
||
$mid_token,
|
||
$options['reply-tag'],
|
||
$body);
|
||
}
|
||
|
||
$txtbody = rtrim(\Format::html2text($body, 90, false))
|
||
. ($messageId ? "\nRef-Mid: $messageId\n" : '');
|
||
$message->setTextBody($txtbody);
|
||
}
|
||
else {
|
||
$message->setTextBody($body);
|
||
$isHtml = false;
|
||
}
|
||
|
||
if ($isHtml && $cfg && $cfg->isRichTextEnabled()) {
|
||
// Pick a domain compatible with pear Mail_Mime
|
||
$matches = array();
|
||
if (preg_match('#(@[0-9a-zA-Z\-\.]+)#', (string) $this->getFromAddress(), $matches)) {
|
||
$domain = $matches[1];
|
||
} else {
|
||
$domain = '@localhost';
|
||
}
|
||
// Format content-ids with the domain, and add the inline images
|
||
// to the email attachment list
|
||
$self = $this;
|
||
$body = preg_replace_callback('/cid:([\w.-]{32})/',
|
||
function($match) use ($domain, $message, $self) {
|
||
if (!($file=$self->getFile($match[1])))
|
||
return $match[0];
|
||
|
||
try {
|
||
$message->addInlineImage($match[1].$domain, $file);
|
||
// Don't re-attach the image below
|
||
unset($self->attachments[$file->getUId()]);
|
||
return $match[0].$domain;
|
||
} catch(\Exception $ex) {
|
||
$self->logWarning(sprintf("%1\$s:%2\$s\n\n%3\$s\n",
|
||
_S("Unable to retrieve email inline image"),
|
||
$match[1].$domain,
|
||
$ex->getMessage()));
|
||
}
|
||
}, $body);
|
||
// Add an HTML body
|
||
$message->setHtmlBody($body);
|
||
}
|
||
//XXX: Attachments
|
||
if(($attachments=$this->getAttachments())) {
|
||
foreach($attachments as $file) {
|
||
if ($file instanceof \Attachment) {
|
||
$filename = $file->getFilename();
|
||
$file = $file->getFile();
|
||
} else {
|
||
$filename = $file->getName();
|
||
}
|
||
|
||
try {
|
||
$message->addAttachment($file, $filename);
|
||
} catch(\Exception $ex) {
|
||
$this->logWarning(sprintf("%1\$s:%2\$s\n\n%3\$s\n",
|
||
_S("Unable to retrieve email attachment"),
|
||
$filename,
|
||
$ex->getMessage()));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Try possible SMTP Accounts - connections are cached per request
|
||
// at the account level.
|
||
foreach ($this->getSmtpAccounts() ?: [] as $smtpAccount) {
|
||
try {
|
||
// Check if SPOOFING is allowed.
|
||
if (!$smtpAccount->allowSpoofing()
|
||
&& strcasecmp($smtpAccount->getEmail()->getEmail(),
|
||
$this->getFromEmail())) {
|
||
// If the Account DOES NOT allow spoofing then set the
|
||
// Sender as the smtp account sending out the email
|
||
// TODO: Allow Aliases to Spoof parent account by
|
||
// default.
|
||
$message->setSender(
|
||
// get Account Email
|
||
(string) $smtpAccount->getEmail()->getEmail(),
|
||
// Try to keep the name if available
|
||
$this->getFromName() ?: $smtpAccount->getName() ?: $this->getEmail());
|
||
}
|
||
// Attempt to send the Message.
|
||
if (($smtp=$smtpAccount->getSmtpConnection())
|
||
&& $smtp->sendMessage($message))
|
||
return $message->getId();
|
||
} catch (\Exception $ex) {
|
||
// Log the SMTP error
|
||
$this->logError(sprintf("%1\$s: %2\$s (%3\$s)\n\n%4\$s\n",
|
||
_S("Unable to email via SMTP"),
|
||
$smtpAccount->getEmail()->getEmail(),
|
||
$smtpAccount->getHostInfo(),
|
||
$ex->getMessage()
|
||
));
|
||
}
|
||
// Attempt Failed: Reset FROM to original email and clear Sender
|
||
$message->setOriginator($this->getFromEmail(), $this->getFromName());
|
||
}
|
||
|
||
// No SMTP or it FAILED....use Sendmail transport (PHP mail())
|
||
// Set Sender / Originator
|
||
if (isset($options['from_address'])) {
|
||
// This is often set via Mailer::sendmail()
|
||
$message->setSender($options['from_address']);
|
||
} elseif (($from=$this->getFromAddress())) {
|
||
// This should be already set but we're making doubly sure
|
||
$message->setOriginator($from->getEmail(), $from->getName());
|
||
}
|
||
|
||
try {
|
||
// ostTicket/Mail/Sendmail transport (mail())
|
||
// Set extra params as needed
|
||
// NOTE: Laminas Mail doesn't respect -f param set Sender (above)
|
||
$args = [];
|
||
$sendmail = new Sendmail($args);
|
||
if ($sendmail->sendMessage($message))
|
||
return $message->getId();
|
||
} catch (\Exception $ex) {
|
||
$this->logError(sprintf("%1\$s\n\n%2\$s\n",
|
||
_S("Unable to email via Sendmail"),
|
||
$ex->getMessage()
|
||
));
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function log($msg, $type='warning') {
|
||
global $ost;
|
||
|
||
if (!$ost || !$msg)
|
||
return;
|
||
|
||
// No email alerts on email errors
|
||
switch ($type) {
|
||
case 'error':
|
||
return $ost->logError(_S('Mailer Error'), $msg, false);
|
||
break;
|
||
case 'warning':
|
||
default:
|
||
return $ost->logWarning(_S('Mailer Warning'), $msg, false);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function logError($error) {
|
||
return $this->log($error, 'error');
|
||
}
|
||
|
||
function logWarning($warning) {
|
||
return $this->log($warning, 'warning');
|
||
}
|
||
|
||
//Emails using native php mail function - if DB connection doesn't exist.
|
||
//Don't use this function if you can help it.
|
||
static function sendmail($to, $subject, $message, $from=null, $options=null) {
|
||
$mailer = new Mailer(null, array('notice'=>true, 'nobounce'=>true));
|
||
$mailer->setFromAddress($from, $options['from_name'] ?: null);
|
||
return $mailer->send($to, $subject, $message, $options);
|
||
}
|
||
}
|