no message
This commit is contained in:
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "nette/mail",
|
||||
"description": "📧 Nette Mail: handy email creation and transfer library for PHP with both text and MIME-compliant support.",
|
||||
"keywords": ["nette", "mail", "mailer", "smtp", "mime"],
|
||||
"homepage": "https://nette.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"ext-iconv": "*",
|
||||
"nette/utils": "^2.4 || ~3.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/di": "^2.4 || ~3.0.0",
|
||||
"nette/tester": "^2.0",
|
||||
"tracy/tracy": "^2.4"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/nette": "<2.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-fileinfo": "to detect type of attached files"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Framework under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
The BSD License is recommended for most projects. It is easy to understand and it
|
||||
places almost no restrictions on what you can do with the framework. If the GPL
|
||||
fits better to your project, you can use the framework under this license.
|
||||
|
||||
You don't have to notify anyone which license you are using. You can freely
|
||||
use Nette Framework in commercial projects as long as the copyright header
|
||||
remains intact.
|
||||
|
||||
Please be advised that the name "Nette Framework" is a protected trademark and its
|
||||
usage has some limitations. So please do not use word "Nette" in the name of your
|
||||
project or top-level domain, and choose a name that stands on its own merits.
|
||||
If your stuff is good, it will not take long to establish a reputation for yourselves.
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Framework" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
Nette Mail: Sending E-mails
|
||||
===========================
|
||||
|
||||
[](https://packagist.org/packages/nette/mail)
|
||||
[](https://travis-ci.org/nette/mail)
|
||||
[](https://coveralls.io/github/nette/mail?branch=master)
|
||||
[](https://github.com/nette/mail/releases)
|
||||
[](https://github.com/nette/mail/blob/master/license.md)
|
||||
|
||||
Almost every web application needs to send e-mails, whether newsletters or order confirmations. That's why Nette Framework provides necessary tools.
|
||||
|
||||
Example of creating an e-mail using `Nette\Mail\Message` class:
|
||||
|
||||
```php
|
||||
use Nette\Mail\Message;
|
||||
|
||||
$mail = new Message;
|
||||
$mail->setFrom('John <john@example.com>')
|
||||
->addTo('peter@example.com')
|
||||
->addTo('jack@example.com')
|
||||
->setSubject('Order Confirmation')
|
||||
->setBody("Hello, Your order has been accepted.");
|
||||
```
|
||||
|
||||
All parameters must be encoded in UTF-8.
|
||||
|
||||
And sending:
|
||||
|
||||
```php
|
||||
use Nette\Mail\SendmailMailer;
|
||||
|
||||
$mailer = new SendmailMailer;
|
||||
$mailer->send($mail);
|
||||
```
|
||||
|
||||
In addition to specifying recipient with `addTo()`, it's possible to specify recipient of copy with `addCc()` and recipient of blind copy: `addBcc()`.
|
||||
In all these methods, including `setFrom()`, we can specifiy addressee in three ways:
|
||||
|
||||
```php
|
||||
$mail->setFrom('john.doe@example.com');
|
||||
$mail->setFrom('john.doe@example.com', 'John Doe');
|
||||
$mail->setFrom('John Doe <john.doe@example.com>');
|
||||
```
|
||||
|
||||
HTML content can be defined using `setHtmlBody()` method:
|
||||
|
||||
```php
|
||||
$mail->setHTMLBody('<b>Sample HTML</b> <img src="background.gif">');
|
||||
```
|
||||
|
||||
Embedded images can be inserted using `$mail->addEmbeddedFile('background.gif')`, but it is not necessary.
|
||||
Why? Because Nette Framework finds and inserts all files referenced in the HTML code automatically.
|
||||
This behavior can be supressed by adding `false` as a second parameter of the `setHtmlBody()` method.
|
||||
|
||||
If a HTML e-mail has no plain-text alternative, it will be automatically generated. And if it has no subject set, it will be taken from the `<title>` element.
|
||||
|
||||
Of course, it's possible to add attachments to the e-mail:
|
||||
|
||||
```php
|
||||
$mail->addAttachment('example.zip');
|
||||
```
|
||||
|
||||
Can e-mail sending be even easier?
|
||||
|
||||
|
||||
Custom mailer
|
||||
-------------
|
||||
|
||||
Default mailer uses PHP function `mail`. If you need to send mail through a SMTP server, you can use `SmtpMailer`.
|
||||
|
||||
```php
|
||||
$mailer = new Nette\Mail\SmtpMailer([
|
||||
'host' => 'smtp.gmail.com',
|
||||
'username' => 'john@gmail.com',
|
||||
'password' => '*****',
|
||||
'secure' => 'ssl',
|
||||
'context' => [
|
||||
'ssl' => [
|
||||
'capath' => '/path/to/my/trusted/ca/folder',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$mailer->send($mail);
|
||||
```
|
||||
|
||||
You can also create your own mailer - it's a class implementing Nette\Mail\IMailer interface.
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Bridges\MailDI;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Mail extension for Nette DI.
|
||||
*/
|
||||
class MailExtension extends Nette\DI\CompilerExtension
|
||||
{
|
||||
public $defaults = [
|
||||
'smtp' => false,
|
||||
'host' => null,
|
||||
'port' => null,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'secure' => null,
|
||||
'timeout' => null,
|
||||
'clientHost' => null,
|
||||
'persistent' => false,
|
||||
];
|
||||
|
||||
|
||||
public function loadConfiguration()
|
||||
{
|
||||
$builder = $this->getContainerBuilder();
|
||||
$config = $this->validateConfig($this->defaults);
|
||||
|
||||
$mailer = $builder->addDefinition($this->prefix('mailer'))
|
||||
->setClass(Nette\Mail\IMailer::class);
|
||||
|
||||
if (empty($config['smtp'])) {
|
||||
$mailer->setFactory(Nette\Mail\SendmailMailer::class);
|
||||
} else {
|
||||
$mailer->setFactory(Nette\Mail\SmtpMailer::class, [$config]);
|
||||
}
|
||||
|
||||
if ($this->name === 'mail') {
|
||||
$builder->addAlias('nette.mailer', $this->prefix('mailer'));
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
class FallbackMailer implements IMailer
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var callable[] function (FallbackMailer $sender, SendException $e, IMailer $mailer, Message $mail) */
|
||||
public $onFailure;
|
||||
|
||||
/** @var IMailer[] */
|
||||
private $mailers;
|
||||
|
||||
/** @var int */
|
||||
private $retryCount;
|
||||
|
||||
/** @var int in miliseconds */
|
||||
private $retryWaitTime;
|
||||
|
||||
|
||||
/**
|
||||
* @param IMailer[]
|
||||
* @param int
|
||||
* @param int in miliseconds
|
||||
*/
|
||||
public function __construct(array $mailers, $retryCount = 3, $retryWaitTime = 1000)
|
||||
{
|
||||
$this->mailers = $mailers;
|
||||
$this->retryCount = $retryCount;
|
||||
$this->retryWaitTime = $retryWaitTime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends email.
|
||||
* @return void
|
||||
* @throws FallbackMailerException
|
||||
*/
|
||||
public function send(Message $mail)
|
||||
{
|
||||
if (!$this->mailers) {
|
||||
throw new Nette\InvalidArgumentException('At least one mailer must be provided.');
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $this->retryCount; $i++) {
|
||||
if ($i > 0) {
|
||||
usleep($this->retryWaitTime * 1000);
|
||||
}
|
||||
|
||||
foreach ($this->mailers as $mailer) {
|
||||
try {
|
||||
$mailer->send($mail);
|
||||
return;
|
||||
|
||||
} catch (SendException $e) {
|
||||
$failures[] = $e;
|
||||
$this->onFailure($this, $e, $mailer, $mail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$e = new FallbackMailerException('All mailers failed to send the message.');
|
||||
$e->failures = $failures;
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function addMailer(IMailer $mailer)
|
||||
{
|
||||
$this->mailers[] = $mailer;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
|
||||
/**
|
||||
* Mailer interface.
|
||||
*/
|
||||
interface IMailer
|
||||
{
|
||||
|
||||
/**
|
||||
* Sends email.
|
||||
* @return void
|
||||
* @throws SendException
|
||||
*/
|
||||
function send(Message $mail);
|
||||
}
|
||||
+444
@@ -0,0 +1,444 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
use Nette;
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
|
||||
/**
|
||||
* Mail provides functionality to compose and send both text and MIME-compliant multipart email messages.
|
||||
*
|
||||
* @property string $subject
|
||||
* @property string $htmlBody
|
||||
*/
|
||||
class Message extends MimePart
|
||||
{
|
||||
/** Priority */
|
||||
const HIGH = 1,
|
||||
NORMAL = 3,
|
||||
LOW = 5;
|
||||
|
||||
/** @var array */
|
||||
public static $defaultHeaders = [
|
||||
'MIME-Version' => '1.0',
|
||||
'X-Mailer' => 'Nette Framework',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
private $attachments = [];
|
||||
|
||||
/** @var array */
|
||||
private $inlines = [];
|
||||
|
||||
/** @var string */
|
||||
private $htmlBody = '';
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
foreach (static::$defaultHeaders as $name => $value) {
|
||||
$this->setHeader($name, $value);
|
||||
}
|
||||
$this->setHeader('Date', date('r'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the sender of the message.
|
||||
* @param string email or format "John Doe" <doe@example.com>
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setFrom($email, $name = null)
|
||||
{
|
||||
$this->setHeader('From', $this->formatEmail($email, $name));
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sender of the message.
|
||||
* @return array
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->getHeader('From');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the reply-to address.
|
||||
* @param string email or format "John Doe" <doe@example.com>
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function addReplyTo($email, $name = null)
|
||||
{
|
||||
$this->setHeader('Reply-To', $this->formatEmail($email, $name), true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the subject of the message.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setSubject($subject)
|
||||
{
|
||||
$this->setHeader('Subject', $subject);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the subject of the message.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->getHeader('Subject');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds email recipient.
|
||||
* @param string email or format "John Doe" <doe@example.com>
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function addTo($email, $name = null) // addRecipient()
|
||||
{
|
||||
$this->setHeader('To', $this->formatEmail($email, $name), true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds carbon copy email recipient.
|
||||
* @param string email or format "John Doe" <doe@example.com>
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function addCc($email, $name = null)
|
||||
{
|
||||
$this->setHeader('Cc', $this->formatEmail($email, $name), true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds blind carbon copy email recipient.
|
||||
* @param string email or format "John Doe" <doe@example.com>
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function addBcc($email, $name = null)
|
||||
{
|
||||
$this->setHeader('Bcc', $this->formatEmail($email, $name), true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats recipient email.
|
||||
* @param string
|
||||
* @param string|null
|
||||
* @return array
|
||||
*/
|
||||
private function formatEmail($email, $name)
|
||||
{
|
||||
if (!$name && preg_match('#^(.+) +<(.*)>\z#', $email, $matches)) {
|
||||
return [$matches[2] => $matches[1]];
|
||||
} else {
|
||||
return [$email => $name];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the Return-Path header of the message.
|
||||
* @param string email
|
||||
* @return static
|
||||
*/
|
||||
public function setReturnPath($email)
|
||||
{
|
||||
$this->setHeader('Return-Path', $email);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Return-Path header.
|
||||
* @return string
|
||||
*/
|
||||
public function getReturnPath()
|
||||
{
|
||||
return $this->getHeader('Return-Path');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets email priority.
|
||||
* @param int
|
||||
* @return static
|
||||
*/
|
||||
public function setPriority($priority)
|
||||
{
|
||||
$this->setHeader('X-Priority', (int) $priority);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns email priority.
|
||||
* @return int
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return $this->getHeader('X-Priority');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets HTML body.
|
||||
* @param string
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setHtmlBody($html, $basePath = null)
|
||||
{
|
||||
$html = (string) $html;
|
||||
|
||||
if ($basePath) {
|
||||
$cids = [];
|
||||
$matches = Strings::matchAll(
|
||||
$html,
|
||||
'#
|
||||
(<img[^<>]*\s src\s*=\s*
|
||||
|<body[^<>]*\s background\s*=\s*
|
||||
|<[^<>]+\s style\s*=\s* ["\'][^"\'>]+[:\s] url\(
|
||||
|<style[^>]*>[^<]+ [:\s] url\()
|
||||
(["\']?)(?![a-z]+:|[/\\#])([^"\'>)\s]+)
|
||||
|\[\[ ([\w()+./@~-]+) \]\]
|
||||
#ix',
|
||||
PREG_OFFSET_CAPTURE
|
||||
);
|
||||
foreach (array_reverse($matches) as $m) {
|
||||
$file = rtrim($basePath, '/\\') . '/' . (isset($m[4]) ? $m[4][0] : urldecode($m[3][0]));
|
||||
if (!isset($cids[$file])) {
|
||||
$cids[$file] = substr($this->addEmbeddedFile($file)->getHeader('Content-ID'), 1, -1);
|
||||
}
|
||||
$html = substr_replace($html,
|
||||
"{$m[1][0]}{$m[2][0]}cid:{$cids[$file]}",
|
||||
$m[0][1], strlen($m[0][0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getSubject() == null) { // intentionally ==
|
||||
$html = Strings::replace($html, '#<title>(.+?)</title>#is', function ($m) {
|
||||
$this->setSubject(html_entity_decode($m[1], ENT_QUOTES, 'UTF-8'));
|
||||
});
|
||||
}
|
||||
|
||||
$this->htmlBody = ltrim(str_replace("\r", '', $html), "\n");
|
||||
|
||||
if ($this->getBody() === '' && $html !== '') {
|
||||
$this->setBody($this->buildText($html));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets HTML body.
|
||||
* @return string
|
||||
*/
|
||||
public function getHtmlBody()
|
||||
{
|
||||
return $this->htmlBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds embedded file.
|
||||
* @param string
|
||||
* @param string
|
||||
* @param string
|
||||
* @return MimePart
|
||||
*/
|
||||
public function addEmbeddedFile($file, $content = null, $contentType = null)
|
||||
{
|
||||
return $this->inlines[$file] = $this->createAttachment($file, $content, $contentType, 'inline')
|
||||
->setHeader('Content-ID', $this->getRandomId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds inlined Mime Part.
|
||||
* @param MimePart
|
||||
* @return static
|
||||
*/
|
||||
public function addInlinePart(MimePart $part)
|
||||
{
|
||||
$this->inlines[] = $part;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds attachment.
|
||||
* @param string
|
||||
* @param string
|
||||
* @param string
|
||||
* @return MimePart
|
||||
*/
|
||||
public function addAttachment($file, $content = null, $contentType = null)
|
||||
{
|
||||
return $this->attachments[] = $this->createAttachment($file, $content, $contentType, 'attachment');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets all email attachments.
|
||||
* @return MimePart[]
|
||||
*/
|
||||
public function getAttachments()
|
||||
{
|
||||
return $this->attachments;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates file MIME part.
|
||||
* @param string
|
||||
* @param string|null
|
||||
* @param string|null
|
||||
* @param string
|
||||
* @return MimePart
|
||||
*/
|
||||
private function createAttachment($file, $content, $contentType, $disposition)
|
||||
{
|
||||
$part = new MimePart;
|
||||
if ($content === null) {
|
||||
$content = @file_get_contents($file); // @ is escalated to exception
|
||||
if ($content === false) {
|
||||
throw new Nette\FileNotFoundException("Unable to read file '$file'.");
|
||||
}
|
||||
} else {
|
||||
$content = (string) $content;
|
||||
}
|
||||
if (!$contentType) {
|
||||
$contentType = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content);
|
||||
}
|
||||
if (!strcasecmp($contentType, 'message/rfc822')) { // not allowed for attached files
|
||||
$contentType = 'application/octet-stream';
|
||||
} elseif (!strcasecmp($contentType, 'image/svg')) { // Troublesome for some mailers...
|
||||
$contentType = 'image/svg+xml';
|
||||
}
|
||||
|
||||
$part->setBody($content);
|
||||
$part->setContentType($contentType);
|
||||
$part->setEncoding(preg_match('#(multipart|message)/#A', $contentType) ? self::ENCODING_8BIT : self::ENCODING_BASE64);
|
||||
$part->setHeader('Content-Disposition', $disposition . '; filename="' . Strings::fixEncoding(basename($file)) . '"');
|
||||
return $part;
|
||||
}
|
||||
|
||||
|
||||
/********************* building and sending ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns encoded message.
|
||||
* @return string
|
||||
*/
|
||||
public function generateMessage()
|
||||
{
|
||||
return $this->build()->getEncodedMessage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds email. Does not modify itself, but returns a new object.
|
||||
* @return static
|
||||
*/
|
||||
protected function build()
|
||||
{
|
||||
$mail = clone $this;
|
||||
$mail->setHeader('Message-ID', $this->getRandomId());
|
||||
|
||||
$cursor = $mail;
|
||||
if ($mail->attachments) {
|
||||
$tmp = $cursor->setContentType('multipart/mixed');
|
||||
$cursor = $cursor->addPart();
|
||||
foreach ($mail->attachments as $value) {
|
||||
$tmp->addPart($value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($mail->htmlBody !== '') {
|
||||
$tmp = $cursor->setContentType('multipart/alternative');
|
||||
$cursor = $cursor->addPart();
|
||||
$alt = $tmp->addPart();
|
||||
if ($mail->inlines) {
|
||||
$tmp = $alt->setContentType('multipart/related');
|
||||
$alt = $alt->addPart();
|
||||
foreach ($mail->inlines as $value) {
|
||||
$tmp->addPart($value);
|
||||
}
|
||||
}
|
||||
$alt->setContentType('text/html', 'UTF-8')
|
||||
->setEncoding(preg_match('#[^\n]{990}#', $mail->htmlBody)
|
||||
? self::ENCODING_QUOTED_PRINTABLE
|
||||
: (preg_match('#[\x80-\xFF]#', $mail->htmlBody) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
|
||||
->setBody($mail->htmlBody);
|
||||
}
|
||||
|
||||
$text = $mail->getBody();
|
||||
$mail->setBody('');
|
||||
$cursor->setContentType('text/plain', 'UTF-8')
|
||||
->setEncoding(preg_match('#[^\n]{990}#', $text)
|
||||
? self::ENCODING_QUOTED_PRINTABLE
|
||||
: (preg_match('#[\x80-\xFF]#', $text) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
|
||||
->setBody($text);
|
||||
|
||||
return $mail;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds text content.
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
protected function buildText($html)
|
||||
{
|
||||
$text = Strings::replace($html, [
|
||||
'#<(style|script|head).*</\\1>#Uis' => '',
|
||||
'#<t[dh][ >]#i' => ' $0',
|
||||
'#<a\s[^>]*href=(?|"([^"]+)"|\'([^\']+)\')[^>]*>(.*?)</a>#is' => '$2 <$1>',
|
||||
'#[\r\n]+#' => ' ',
|
||||
'#<(/?p|/?h\d|li|br|/tr)[ >/]#i' => "\n$0",
|
||||
]);
|
||||
$text = html_entity_decode(strip_tags($text), ENT_QUOTES, 'UTF-8');
|
||||
$text = Strings::replace($text, '#[ \t]+#', ' ');
|
||||
return trim($text);
|
||||
}
|
||||
|
||||
|
||||
/** @return string */
|
||||
private function getRandomId()
|
||||
{
|
||||
return '<' . Nette\Utils\Random::generate() . '@'
|
||||
. preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n'))
|
||||
. '>';
|
||||
}
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
use Nette;
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
|
||||
/**
|
||||
* MIME message part.
|
||||
*
|
||||
* @property string $body
|
||||
*/
|
||||
class MimePart
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** encoding */
|
||||
const ENCODING_BASE64 = 'base64',
|
||||
ENCODING_7BIT = '7bit',
|
||||
ENCODING_8BIT = '8bit',
|
||||
ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
|
||||
|
||||
/** @internal */
|
||||
const EOL = "\r\n";
|
||||
|
||||
const LINE_LENGTH = 76;
|
||||
|
||||
/** @var array */
|
||||
private $headers = [];
|
||||
|
||||
/** @var array */
|
||||
private $parts = [];
|
||||
|
||||
/** @var string */
|
||||
private $body = '';
|
||||
|
||||
|
||||
/**
|
||||
* Sets a header.
|
||||
* @param string
|
||||
* @param string|array value or pair email => name
|
||||
* @param bool
|
||||
* @return static
|
||||
*/
|
||||
public function setHeader($name, $value, $append = false)
|
||||
{
|
||||
if (!$name || preg_match('#[^a-z0-9-]#i', $name)) {
|
||||
throw new Nette\InvalidArgumentException("Header name must be non-empty alphanumeric string, '$name' given.");
|
||||
}
|
||||
|
||||
if ($value == null) { // intentionally ==
|
||||
if (!$append) {
|
||||
unset($this->headers[$name]);
|
||||
}
|
||||
|
||||
} elseif (is_array($value)) { // email
|
||||
$tmp = &$this->headers[$name];
|
||||
if (!$append || !is_array($tmp)) {
|
||||
$tmp = [];
|
||||
}
|
||||
|
||||
foreach ($value as $email => $recipient) {
|
||||
if ($recipient === null) {
|
||||
// continue
|
||||
} elseif (!Strings::checkEncoding($recipient)) {
|
||||
Nette\Utils\Validators::assert($recipient, 'unicode', "header '$name'");
|
||||
} elseif (preg_match('#[\r\n]#', $recipient)) {
|
||||
throw new Nette\InvalidArgumentException('Name must not contain line separator.');
|
||||
}
|
||||
Nette\Utils\Validators::assert($email, 'email', "header '$name'");
|
||||
$tmp[$email] = $recipient;
|
||||
}
|
||||
|
||||
} else {
|
||||
$value = (string) $value;
|
||||
if (!Strings::checkEncoding($value)) {
|
||||
throw new Nette\InvalidArgumentException('Header is not valid UTF-8 string.');
|
||||
}
|
||||
$this->headers[$name] = preg_replace('#[\r\n]+#', ' ', $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a header.
|
||||
* @param string
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHeader($name)
|
||||
{
|
||||
return isset($this->headers[$name]) ? $this->headers[$name] : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a header.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function clearHeader($name)
|
||||
{
|
||||
unset($this->headers[$name]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an encoded header.
|
||||
* @param string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEncodedHeader($name)
|
||||
{
|
||||
$offset = strlen($name) + 2; // colon + space
|
||||
|
||||
if (!isset($this->headers[$name])) {
|
||||
return null;
|
||||
|
||||
} elseif (is_array($this->headers[$name])) {
|
||||
$s = '';
|
||||
foreach ($this->headers[$name] as $email => $name) {
|
||||
if ($name != null) { // intentionally ==
|
||||
$s .= self::encodeHeader($name, $offset, true);
|
||||
$email = " <$email>";
|
||||
}
|
||||
$s .= self::append($email . ',', $offset);
|
||||
}
|
||||
return ltrim(substr($s, 0, -1)); // last comma
|
||||
|
||||
} elseif (preg_match('#^(\S+; (?:file)?name=)"(.*)"\z#', $this->headers[$name], $m)) { // Content-Disposition
|
||||
$offset += strlen($m[1]);
|
||||
return $m[1] . '"' . self::encodeHeader($m[2], $offset) . '"';
|
||||
|
||||
} else {
|
||||
return ltrim(self::encodeHeader($this->headers[$name], $offset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all headers.
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets Content-Type header.
|
||||
* @param string
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setContentType($contentType, $charset = null)
|
||||
{
|
||||
$this->setHeader('Content-Type', $contentType . ($charset ? "; charset=$charset" : ''));
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets Content-Transfer-Encoding header.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->setHeader('Content-Transfer-Encoding', $encoding);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns Content-Transfer-Encoding header.
|
||||
* @return string
|
||||
*/
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->getHeader('Content-Transfer-Encoding');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds or creates new multipart.
|
||||
* @return self
|
||||
*/
|
||||
public function addPart(self $part = null)
|
||||
{
|
||||
return $this->parts[] = $part === null ? new self : $part;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets textual body.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setBody($body)
|
||||
{
|
||||
$this->body = (string) $body;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets textual body.
|
||||
* @return string
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
|
||||
/********************* building ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns encoded message.
|
||||
* @return string
|
||||
*/
|
||||
public function getEncodedMessage()
|
||||
{
|
||||
$output = '';
|
||||
$boundary = '--------' . Nette\Utils\Random::generate();
|
||||
|
||||
foreach ($this->headers as $name => $value) {
|
||||
$output .= $name . ': ' . $this->getEncodedHeader($name);
|
||||
if ($this->parts && $name === 'Content-Type') {
|
||||
$output .= ';' . self::EOL . "\tboundary=\"$boundary\"";
|
||||
}
|
||||
$output .= self::EOL;
|
||||
}
|
||||
$output .= self::EOL;
|
||||
|
||||
$body = $this->body;
|
||||
if ($body !== '') {
|
||||
switch ($this->getEncoding()) {
|
||||
case self::ENCODING_QUOTED_PRINTABLE:
|
||||
$output .= quoted_printable_encode($body);
|
||||
break;
|
||||
|
||||
case self::ENCODING_BASE64:
|
||||
$output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL));
|
||||
break;
|
||||
|
||||
case self::ENCODING_7BIT:
|
||||
$body = preg_replace('#[\x80-\xFF]+#', '', $body);
|
||||
// break omitted
|
||||
|
||||
case self::ENCODING_8BIT:
|
||||
$body = str_replace(["\x00", "\r"], '', $body);
|
||||
$body = str_replace("\n", self::EOL, $body);
|
||||
$output .= $body;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Nette\InvalidStateException('Unknown encoding.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->parts) {
|
||||
if (substr($output, -strlen(self::EOL)) !== self::EOL) {
|
||||
$output .= self::EOL;
|
||||
}
|
||||
foreach ($this->parts as $part) {
|
||||
$output .= '--' . $boundary . self::EOL . $part->getEncodedMessage() . self::EOL;
|
||||
}
|
||||
$output .= '--' . $boundary . '--';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/********************* QuotedPrintable helpers ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Converts a 8 bit header to a string.
|
||||
* @param string
|
||||
* @param int
|
||||
* @param bool
|
||||
* @return string
|
||||
*/
|
||||
private static function encodeHeader($s, &$offset = 0, $quotes = false)
|
||||
{
|
||||
if (strspn($s, "!\"#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^`abcdefghijklmnopqrstuvwxyz{|}~=? _\r\n\t") === strlen($s)) {
|
||||
if ($quotes && preg_match('#[^ a-zA-Z0-9!\#$%&\'*+/?^_`{|}~-]#', $s)) { // RFC 2822 atext except =
|
||||
return self::append('"' . addcslashes($s, '"\\') . '"', $offset);
|
||||
}
|
||||
return self::append($s, $offset);
|
||||
}
|
||||
|
||||
$o = '';
|
||||
if ($offset >= 55) { // maximum for iconv_mime_encode
|
||||
$o = self::EOL . "\t";
|
||||
$offset = 1;
|
||||
}
|
||||
|
||||
$s = iconv_mime_encode(str_repeat(' ', $old = $offset), $s, [
|
||||
'scheme' => 'B', // Q is broken
|
||||
'input-charset' => 'UTF-8',
|
||||
'output-charset' => 'UTF-8',
|
||||
]);
|
||||
|
||||
$offset = strlen($s) - strrpos($s, "\n");
|
||||
$s = str_replace("\n ", "\n\t", substr($s, $old + 2)); // adds ': '
|
||||
return $o . $s;
|
||||
}
|
||||
|
||||
|
||||
private static function append($s, &$offset = 0)
|
||||
{
|
||||
if ($offset + strlen($s) > self::LINE_LENGTH) {
|
||||
$offset = 1;
|
||||
$s = self::EOL . "\t" . $s;
|
||||
}
|
||||
$offset += strlen($s);
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Sends emails via the PHP internal mail() function.
|
||||
*/
|
||||
class SendmailMailer implements IMailer
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string|null */
|
||||
public $commandArgs;
|
||||
|
||||
|
||||
/**
|
||||
* Sends email.
|
||||
* @return void
|
||||
* @throws SendException
|
||||
*/
|
||||
public function send(Message $mail)
|
||||
{
|
||||
if (!function_exists('mail')) {
|
||||
throw new SendException('Unable to send email: mail() has been disabled.');
|
||||
}
|
||||
$tmp = clone $mail;
|
||||
$tmp->setHeader('Subject', null);
|
||||
$tmp->setHeader('To', null);
|
||||
|
||||
$parts = explode(Message::EOL . Message::EOL, $tmp->generateMessage(), 2);
|
||||
|
||||
$args = [
|
||||
str_replace(Message::EOL, PHP_EOL, $mail->getEncodedHeader('To')),
|
||||
str_replace(Message::EOL, PHP_EOL, $mail->getEncodedHeader('Subject')),
|
||||
str_replace(Message::EOL, PHP_EOL, $parts[1]),
|
||||
str_replace(Message::EOL, PHP_EOL, $parts[0]),
|
||||
];
|
||||
if ($this->commandArgs) {
|
||||
$args[] = (string) $this->commandArgs;
|
||||
}
|
||||
$res = Nette\Utils\Callback::invokeSafe('mail', $args, function ($message) use (&$info) {
|
||||
$info = ": $message";
|
||||
});
|
||||
if ($res === false) {
|
||||
throw new SendException("Unable to send email$info.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Sends emails via the SMTP server.
|
||||
*/
|
||||
class SmtpMailer implements IMailer
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var resource */
|
||||
private $connection;
|
||||
|
||||
/** @var string */
|
||||
private $host;
|
||||
|
||||
/** @var int */
|
||||
private $port;
|
||||
|
||||
/** @var string */
|
||||
private $username;
|
||||
|
||||
/** @var string */
|
||||
private $password;
|
||||
|
||||
/** @var string ssl | tls | (empty) */
|
||||
private $secure;
|
||||
|
||||
/** @var int */
|
||||
private $timeout;
|
||||
|
||||
/** @var resource */
|
||||
private $context;
|
||||
|
||||
/** @var bool */
|
||||
private $persistent;
|
||||
|
||||
/** @var string */
|
||||
private $clientHost;
|
||||
|
||||
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (isset($options['host'])) {
|
||||
$this->host = $options['host'];
|
||||
$this->port = isset($options['port']) ? (int) $options['port'] : null;
|
||||
} else {
|
||||
$this->host = ini_get('SMTP');
|
||||
$this->port = (int) ini_get('smtp_port');
|
||||
}
|
||||
$this->username = isset($options['username']) ? $options['username'] : '';
|
||||
$this->password = isset($options['password']) ? $options['password'] : '';
|
||||
$this->secure = isset($options['secure']) ? $options['secure'] : '';
|
||||
$this->timeout = isset($options['timeout']) ? (int) $options['timeout'] : 20;
|
||||
$this->context = isset($options['context']) ? stream_context_create($options['context']) : stream_context_get_default();
|
||||
if (!$this->port) {
|
||||
$this->port = $this->secure === 'ssl' ? 465 : 25;
|
||||
}
|
||||
$this->persistent = !empty($options['persistent']);
|
||||
if (isset($options['clientHost'])) {
|
||||
$this->clientHost = $options['clientHost'];
|
||||
} else {
|
||||
$this->clientHost = isset($_SERVER['HTTP_HOST']) && preg_match('#^[\w.-]+\z#', $_SERVER['HTTP_HOST'])
|
||||
? $_SERVER['HTTP_HOST']
|
||||
: 'localhost';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends email.
|
||||
* @return void
|
||||
* @throws SmtpException
|
||||
*/
|
||||
public function send(Message $mail)
|
||||
{
|
||||
$tmp = clone $mail;
|
||||
$tmp->setHeader('Bcc', null);
|
||||
$data = $tmp->generateMessage();
|
||||
|
||||
try {
|
||||
if (!$this->connection) {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
if (($from = $mail->getHeader('Return-Path'))
|
||||
|| ($from = key($mail->getHeader('From')))
|
||||
) {
|
||||
$this->write("MAIL FROM:<$from>", 250);
|
||||
}
|
||||
|
||||
foreach (array_merge(
|
||||
(array) $mail->getHeader('To'),
|
||||
(array) $mail->getHeader('Cc'),
|
||||
(array) $mail->getHeader('Bcc')
|
||||
) as $email => $name) {
|
||||
$this->write("RCPT TO:<$email>", [250, 251]);
|
||||
}
|
||||
|
||||
$this->write('DATA', 354);
|
||||
$data = preg_replace('#^\.#m', '..', $data);
|
||||
$this->write($data);
|
||||
$this->write('.', 250);
|
||||
|
||||
if (!$this->persistent) {
|
||||
$this->write('QUIT', 221);
|
||||
$this->disconnect();
|
||||
}
|
||||
} catch (SmtpException $e) {
|
||||
if ($this->connection) {
|
||||
$this->disconnect();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connects and authenticates to SMTP server.
|
||||
* @return void
|
||||
*/
|
||||
protected function connect()
|
||||
{
|
||||
$this->connection = @stream_socket_client(// @ is escalated to exception
|
||||
($this->secure === 'ssl' ? 'ssl://' : '') . $this->host . ':' . $this->port,
|
||||
$errno, $error, $this->timeout, STREAM_CLIENT_CONNECT, $this->context
|
||||
);
|
||||
if (!$this->connection) {
|
||||
throw new SmtpException($error ?: error_get_last()['message'], $errno);
|
||||
}
|
||||
stream_set_timeout($this->connection, $this->timeout, 0);
|
||||
$this->read(); // greeting
|
||||
|
||||
$this->write("EHLO $this->clientHost");
|
||||
$ehloResponse = $this->read();
|
||||
if ((int) $ehloResponse !== 250) {
|
||||
$this->write("HELO $this->clientHost", 250);
|
||||
}
|
||||
|
||||
if ($this->secure === 'tls') {
|
||||
$this->write('STARTTLS', 220);
|
||||
if (!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
|
||||
throw new SmtpException('Unable to connect via TLS.');
|
||||
}
|
||||
$this->write("EHLO $this->clientHost", 250);
|
||||
}
|
||||
|
||||
if ($this->username != null && $this->password != null) {
|
||||
$authMechanisms = [];
|
||||
if (preg_match('~^250[ -]AUTH (.*)$~im', $ehloResponse, $matches)) {
|
||||
$authMechanisms = explode(' ', trim($matches[1]));
|
||||
}
|
||||
|
||||
if (in_array('PLAIN', $authMechanisms, true)) {
|
||||
$credentials = $this->username . "\0" . $this->username . "\0" . $this->password;
|
||||
$this->write('AUTH PLAIN ' . base64_encode($credentials), 235, 'PLAIN credentials');
|
||||
} else {
|
||||
$this->write('AUTH LOGIN', 334);
|
||||
$this->write(base64_encode($this->username), 334, 'username');
|
||||
$this->write(base64_encode($this->password), 235, 'password');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects from SMTP server.
|
||||
* @return void
|
||||
*/
|
||||
protected function disconnect()
|
||||
{
|
||||
fclose($this->connection);
|
||||
$this->connection = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes data to server and checks response against expected code if some provided.
|
||||
* @param string
|
||||
* @param int|int[] response code
|
||||
* @param string error message
|
||||
* @return void
|
||||
*/
|
||||
protected function write($line, $expectedCode = null, $message = null)
|
||||
{
|
||||
fwrite($this->connection, $line . Message::EOL);
|
||||
if ($expectedCode) {
|
||||
$response = $this->read();
|
||||
if (!in_array((int) $response, (array) $expectedCode, true)) {
|
||||
throw new SmtpException('SMTP server did not accept ' . ($message ?: $line) . ' with error: ' . trim($response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads response from server.
|
||||
* @return string
|
||||
*/
|
||||
protected function read()
|
||||
{
|
||||
$s = '';
|
||||
while (($line = fgets($this->connection, 1000)) != null) { // intentionally ==
|
||||
$s .= $line;
|
||||
if (substr($line, 3, 1) === ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Mail;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Exception thrown when a mail sending error is encountered.
|
||||
*/
|
||||
class SendException extends Nette\InvalidStateException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SMTP mailer exception.
|
||||
*/
|
||||
class SmtpException extends SendException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class FallbackMailerException extends SendException
|
||||
{
|
||||
/** @var SendException[] */
|
||||
public $failures;
|
||||
}
|
||||
Reference in New Issue
Block a user