Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Mail/Mailer.php
<?php

namespace XF\Mail;

use function
intval, is_array;

class
Mailer
{
   
/**
     * @var Templater
     */
   
protected $templater;

   
/**
     * @var \Swift_Transport
     */
   
protected $defaultTransport;

   
/**
     * @var Styler|null
     */
   
protected $styler;

   
/**
     * @var Queue|null
     */
   
protected $queue;

    protected
$defaultFromEmail;
    protected
$defaultFromName;
    protected
$defaultReturnPath;
    protected
$defaultUseVerp;

    protected
$mailClass = 'XF\Mail\Mail';

    public function
__construct(Templater $templater, \Swift_Transport $defaultTransport, Styler $styler = null, Queue $queue = null)
    {
       
$this->templater = $templater;
       
$this->defaultTransport = $defaultTransport;
       
$this->styler = $styler;
       
$this->queue = $queue;
    }

    public function
getMailClass()
    {
        return
$this->mailClass;
    }

    public function
setMailClass($class)
    {
       
$this->mailClass = $class;
    }

    public function
setDefaultFrom($email, $name = null)
    {
        if (
$email)
        {
           
$this->defaultFromEmail = $email;
           
$this->defaultFromName = $name;
        }
        else
        {
           
$this->defaultFromEmail = null;
           
$this->defaultFromName = null;
        }
    }

    public function
getDefaultFromEmail()
    {
        return
$this->defaultFromEmail;
    }

    public function
getDefaultFromName()
    {
        return
$this->defaultFromName;
    }

    public function
setDefaultReturnPath($email, $useVerp = false)
    {
        if (
$email)
        {
           
$this->defaultReturnPath = $email;
           
$this->defaultUseVerp = $useVerp;
        }
        else
        {
           
$this->defaultReturnPath = null;
           
$this->defaultUseVerp = null;
        }
    }

    public function
getDefaultReturnPath()
    {
        return
$this->defaultReturnPath;
    }

    public function
getDefaultUseVerp()
    {
        return
$this->defaultUseVerp;
    }

   
/**
     * @return \XF\Mail\Mail
     */
   
public function newMail()
    {
       
$mailClass = $this->mailClass;
       
$mail = new $mailClass($this);
       
$this->applyMailDefaults($mail);

        return
$mail;
    }

    public function
applyMailDefaults(Mail $mail)
    {
        if (
$this->defaultFromEmail)
        {
           
$mail->setFrom($this->defaultFromEmail, $this->defaultFromName);
        }
        if (
$this->defaultReturnPath)
        {
           
$mail->setReturnPath($this->defaultReturnPath, $this->defaultUseVerp);
        }
    }

    public function
calculateBounceHmac($toEmail)
    {
        return
substr(hash_hmac('md5', $toEmail, \XF::config('globalSalt')), 0, 8);
    }

    public function
generateTextBody($html)
    {
        if (
$this->styler)
        {
            return
$this->styler->generateTextBody($html);
        }
        else
        {
            return
'';
        }
    }

    public function
renderMailTemplate($name, array $params, \XF\Language $language = null, \XF\Entity\User $toUser = null)
    {
        if (!
$language)
        {
           
$language = \XF::language();
        }

       
$templater = $this->templater;

       
$output = $this->renderPartialMailTemplate($name, $params, $language, $toUser);
       
$parts = $this->pullComponentsFromTemplateOutput($output);

        if (!
$parts['text'] && !$parts['html'])
        {
            throw new \
LogicException("Template mail:$name did not render to anything. It must provide either a text or HTML body.");
        }

       
$containerTemplate = $templater->pageParams['template'] ?? 'MAIL_CONTAINER';
        if (
$containerTemplate)
        {
            if (!
strpos($containerTemplate, ':'))
            {
               
$containerTemplate = 'email:' . $containerTemplate;
            }

           
$containerParams = array_replace($templater->pageParams, $parts);

           
$containerOutput = $templater->renderTemplate($containerTemplate, $containerParams);
           
$containerParts = $this->pullComponentsFromTemplateOutput($containerOutput);
        }
        else
        {
           
$containerParts = ['subject' => '', 'html' => '', 'text' => ''];
        }

       
$subject = $parts['subject'] && $containerParts['subject'] ? $containerParts['subject'] : $parts['subject'];
       
$html = $parts['html'] && $containerParts['html'] ? $containerParts['html'] : $parts['html'];
       
$text = $parts['text'] && $containerParts['text'] ? $containerParts['text'] : $parts['text'];

        if (
$this->styler)
        {
           
$html = $this->styler->styleHtml($html, $containerTemplate ? true : false, $language);
        }

        if (isset(
$templater->pageParams['headers']) && is_array($templater->pageParams['headers']))
        {
           
$headers = $templater->pageParams['headers'];
        }
        else
        {
           
$headers = [];
        }

        return [
           
'subject' => $subject,
           
'html' => $html,
           
'text' => $text,
           
'headers' => $headers
       
];
    }

    public function
renderPartialMailTemplate($name, array $params, \XF\Language $language = null, \XF\Entity\User $toUser = null)
    {
        if (!
$language)
        {
           
$language = \XF::language();
        }

       
$defaultParams = $this->getDefaultTemplateParams($language, $toUser);

       
$templater = $this->templater;
       
$templater->setLanguage($language);
       
$templater->addDefaultParam('xf', $defaultParams);
       
$templater->pageParams = [];

        return
$templater->renderTemplate("email:$name", $params);
    }

    protected function
getDefaultTemplateParams(\XF\Language $language, \XF\Entity\User $toUser = null)
    {
        return [
           
'language' => $language,
           
'isRtl' => $language->isRtl(),
           
'options' => \XF::options(),
           
'toUser' => $toUser
       
];
    }

    protected function
pullComponentsFromTemplateOutput($output)
    {
        if (
preg_match('#<mail:subject>(.*)</mail:subject>#siU', $output, $match))
        {
           
$subject = trim(htmlspecialchars_decode($match[1], ENT_QUOTES));
           
$output = preg_replace('#<mail:subject>.*</mail:subject>#siU', '', $output);
        }
        else
        {
           
$subject = '';
        }

        if (
preg_match('#<mail:text>(.*)</mail:text>#siU', $output, $match))
        {
           
$text = trim(html_entity_decode($match[1], ENT_QUOTES | ENT_HTML401, "utf-8"));
           
$output = preg_replace('#<mail:text>.*</mail:text>#siU', '', $output);
        }
        else
        {
           
$text = '';
        }

        if (
preg_match('#<mail:html>(.*)</mail:html>#siU', $output, $match))
        {
           
$html = trim($match[1]);
        }
        else
        {
           
$html = trim($output);
        }

        if (!
$text && $html)
        {
           
$text = $this->generateTextBody($html);
        }

        return [
           
'subject' => $subject,
           
'html' => $html,
           
'text' => $text
       
];
    }

    public function
send(\Swift_Mime_SimpleMessage $message, \Swift_Transport $transport = null, array $queueEntry = null, $allowRetry = true)
    {
       
$to = $message->getTo();
       
$toEmails = $to ? implode(', ', array_keys($to)) : '[unknown]';

        if (!
$transport)
        {
           
$transport = $this->defaultTransport;
        }

        if (!
$transport->isStarted())
        {
            try
            {
               
$transport->start();
            }
            catch (\
Throwable $e)
            {
                if (
$this->queue && $allowRetry)
                {
                   
$this->queue->queueForRetry($message, $queueEntry);
                }

                \
XF::logException($e, false, "Email to {$toEmails} failed:");
                return
0;
            }
        }

       
$sent = 0;

        try
        {
           
$sent = $transport->send($message, $failedRecipients);
            if (!
$sent)
            {
                throw new \
Swift_TransportException('Unable to send mail.');
            }
        }
        catch (\
Throwable $e)
        {
            if (
$this->queue && $allowRetry)
            {
               
$this->queue->queueForRetry($message, $queueEntry);
            }

            \
XF::logException($e, false, "Email to {$toEmails} failed:");

           
// Any error may put us in an inconsistent state, so we need to reset the connection.
            // Ideally this shouldn't throw anything but it could happen so we just want to swallow
            // all errors and continue on.
           
try
            {
               
$transport->stop();
            }
            catch (\
Throwable $null) {}
        }

        return
$sent;
    }

    public function
queue(\Swift_Mime_SimpleMessage $message)
    {
        if (!
$this->queue)
        {
           
// Queue object not passed in (may be disabled in config.php) so skip straight to send
           
return $this->send($message);
        }
        return
$this->queue->queue($message);
    }

    public function
getDefaultTransport()
    {
        return
$this->defaultTransport;
    }

    public function
setDefaultTransport(\Swift_Transport $transport)
    {
       
$this->defaultTransport = $transport;
    }

    public static function
getTransportFromOption($type, array $config)
    {
        switch (
$type)
        {
            case
'smtp';
               
$transport = new \XF\Mail\SmtpTransport($config['smtpHost']);

                if (!empty(
$config['smtpPort']) && intval($config['smtpPort']) != 0)
                {
                   
$transport->setPort(intval($config['smtpPort']));
                }
                if (!empty(
$config['smtpAuth']) && $config['smtpAuth'] != 'none')
                {
                   
$transport->setUsername(
                        !empty(
$config['smtpLoginUsername']) ? $config['smtpLoginUsername'] : ''
                   
);

                    if (!empty(
$config['oauth']))
                    {
                       
$transport->setAuthMode('xoauth2');
                       
$transport->setPassword($config['smtpLoginPassword']);
                    }
                    else
                    {
                       
$transport->setPassword(
                            !empty(
$config['smtpLoginPassword']) ? $config['smtpLoginPassword'] : ''
                       
);
                    }
                }
                if (!empty(
$config['smtpEncrypt']) && $config['smtpEncrypt'] != 'none')
                {
                   
$transport->setEncryption($config['smtpEncrypt']);
                }

               
$transport->registerPlugin(new \Swift_Plugins_AntiFloodPlugin(99));

                return
$transport;

            case
'file':
               
$transport = new \XF\Mail\FileTransport(
                    \
Swift_DependencyContainer::getInstance()->lookup('transport.eventdispatcher')
                );
                if (!empty(
$config['path']))
                {
                   
$transport->setSavePath($config['path']);
                }

                return
$transport;

            case
'sendmail':
            default:
                if (
strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
                {
                   
$iniSmtpHost = ini_get('SMTP');
                   
$iniSmtpPort = ini_get('smtp_port');
                   
$transport = new \XF\Mail\SmtpTransport($iniSmtpHost ?: 'localhost', $iniSmtpPort ?: 25);
                }
                else
                {
                   
$sendmailPath = \XF::app()->config('sendmailPath');
                    if (!
$sendmailPath)
                    {
                       
$sendmailPath = ini_get('sendmail_path');
                    }

                    if (
$sendmailPath && !preg_match('# -(t|bs)#', $sendmailPath))
                    {
                       
// SwiftMailer requires -t or -bs, so if there isn't one, add -t automatically to prevent errors
                       
$sendmailPath .= ' -t';
                    }

                    if (
preg_match('/(.*)-f\s?[^@]+@[^\s]+(.*)$/', $sendmailPath, $matches))
                    {
                       
// if the sendmail path already contains the -f parameter, SwiftMailer won't override it in which
                        // case, we should remove it by default so it can be set automatically to the appropriate value
                       
$sendmailPath = trim(rtrim($matches[1]) . $matches[2]);
                    }

                    if (!
$sendmailPath)
                    {
                       
$sendmailPath = '/usr/sbin/sendmail -t -i';
                    }

                   
$transport = new \Swift_SendmailTransport($sendmailPath);
                }

                return
$transport;
        }
    }
}