Skip to content
4 changes: 4 additions & 0 deletions docs/book/read.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ $mail = new Pop3([
]);
```

If you are connecting to a mail server with a self-signed certificate and want to
skip the SSL verification, you can also pass an additional argument `novalidatecert`
with the value `true`.

Both constructors throw `Laminas\Mail\Exception` or `Laminas\Mail\Protocol\Exception`
(extends `Laminas\Mail\Exception`) for connection errors, depending on the type of
error encountered.
Expand Down
27 changes: 8 additions & 19 deletions src/Protocol/Imap.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ class Imap
*/
const TIMEOUT_CONNECTION = 30;

/**
* socket to imap server
* @var resource|null
*/
protected $socket;

/**
* counter for request tag
* @var int
Expand All @@ -34,13 +28,16 @@ class Imap
/**
* Public constructor
*
* @param string $host hostname or IP address of IMAP server, if given connect() is called
* @param int|null $port port of IMAP server, null for default (143 or 993 for ssl)
* @param bool $ssl use ssl? 'SSL', 'TLS' or false
* @param string $host hostname or IP address of IMAP server, if given connect() is called
* @param int|null $port port of IMAP server, null for default (143 or 993 for ssl)
* @param bool $ssl use ssl? 'SSL', 'TLS' or false
* @param bool $novalidatecert set to true to skip SSL certificate validation
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
*/
public function __construct($host = '', $port = null, $ssl = false)
public function __construct($host = '', $port = null, $ssl = false, $novalidatecert = false)
{
$this->setNoValidateCert($novalidatecert);

if ($host) {
$this->connect($host, $port, $ssl);
}
Expand Down Expand Up @@ -87,15 +84,7 @@ public function connect($host, $port = null, $ssl = false)
}
}

ErrorHandler::start();
$this->socket = fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
$error = ErrorHandler::stop();
if (! $this->socket) {
throw new Exception\RuntimeException(sprintf(
'cannot connect to host %s',
($error ? sprintf('; error = %s (errno = %d )', $error->getMessage(), $error->getCode()) : '')
), 0, $error);
}
$this->setSocket($host, $port);

if (! $this->assumedNextLine('* OK')) {
throw new Exception\RuntimeException('host doesn\'t allow connection');
Expand Down
27 changes: 8 additions & 19 deletions src/Protocol/Pop3.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ class Pop3
*/
public $hasTop = null;

/**
* socket to pop3
* @var null|resource
*/
protected $socket;

/**
* greeting timestamp for apop
* @var null|string
Expand All @@ -40,12 +34,15 @@ class Pop3
/**
* Public constructor
*
* @param string $host hostname or IP address of POP3 server, if given connect() is called
* @param int|null $port port of POP3 server, null for default (110 or 995 for ssl)
* @param bool|string $ssl use ssl? 'SSL', 'TLS' or false
* @param string $host hostname or IP address of POP3 server, if given connect() is called
* @param int|null $port port of POP3 server, null for default (110 or 995 for ssl)
* @param bool|string $ssl use ssl? 'SSL', 'TLS' or false
* @param bool $novalidatecert set to true to skip SSL certificate validation
*/
public function __construct($host = '', $port = null, $ssl = false)
public function __construct($host = '', $port = null, $ssl = false, $novalidatecert = false)
{
$this->setNoValidateCert($novalidatecert);

if ($host) {
$this->connect($host, $port, $ssl);
}
Expand Down Expand Up @@ -92,15 +89,7 @@ public function connect($host, $port = null, $ssl = false)
}
}

ErrorHandler::start();
$this->socket = fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
$error = ErrorHandler::stop();
if (! $this->socket) {
throw new Exception\RuntimeException(sprintf(
'cannot connect to host %s',
($error ? sprintf('; error = %s (errno = %d )', $error->getMessage(), $error->getCode()) : '')
), 0, $error);
}
$this->setSocket($host, $port);

$welcome = $this->readResponse();

Expand Down
76 changes: 76 additions & 0 deletions src/Protocol/ProtocolTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
*/
trait ProtocolTrait
{
/**
* If set to true, do not validate the SSL certificate
* @var null|bool
*/
protected $novalidatecert;

public function getCryptoMethod()
{
// Allow the best TLS version(s) we can
Expand All @@ -27,4 +33,74 @@ public function getCryptoMethod()

return $cryptoMethod;
}

/**
* Do not validate SSL certificate
*
* @param bool $novalidatecert Set to true to disable certificate validation
*
* @return Imap|Pop3
*/
public function setNoValidateCert(bool $novalidatecert)
{
$this->novalidatecert = $novalidatecert;
return $this;
}

/**
* Should we validate SSL certificate?
*
* @return bool
*/
public function validateCert()
{
return ! $this->novalidatecert;
}

/**
* Prepare socket options
*
* @return array
*/
private function prepareSocketOptions()
{
return $this->novalidatecert
? [
'ssl' => [
'verify_peer_name' => false,
'verify_peer' => false,
]
] : [];
}

/**
* Setup connection socket
*
* @param string $host hostname or IP address of IMAP server
* @param int|null $port of IMAP server, default is 143 (993 for ssl)
*
* @return void
*/
protected function setSocket($host, $port)
Comment thread
trasher marked this conversation as resolved.
Outdated
{
$socketOptions = [];

Comment thread
trasher marked this conversation as resolved.
Outdated
ErrorHandler::start();
$this->socket = stream_socket_client(
$host . ":" . $port,
$errno,
$errstr,
self::TIMEOUT_CONNECTION,
STREAM_CLIENT_CONNECT,
stream_context_create($this->prepareSocketOptions())
);
$error = ErrorHandler::stop();
Comment thread
trasher marked this conversation as resolved.

if (! $this->socket) {
throw new Exception\RuntimeException(sprintf(
'cannot connect to host %s',
($error ? sprintf('; error = %s (errno = %d )', $error->getMessage(), $error->getCode()) : '')
), 0, $error);
}
}
}
5 changes: 5 additions & 0 deletions src/Storage/Imap.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ public function __construct($params)
$ssl = isset($params->ssl) ? $params->ssl : false;

$this->protocol = new Protocol\Imap();

if (isset($params->novalidatecert)) {
$this->protocol->setNoValidateCert((bool)$params->novalidatecert);
}
Comment thread
trasher marked this conversation as resolved.

$this->protocol->connect($host, $port, $ssl);
if (! $this->protocol->login($params->user, $password)) {
throw new Exception\RuntimeException('cannot login, user or password wrong');
Expand Down
5 changes: 5 additions & 0 deletions src/Storage/Pop3.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ public function __construct($params)
$ssl = isset($params->ssl) ? $params->ssl : false;

$this->protocol = new Protocol\Pop3();

if (isset($params->novalidatecert)) {
$this->protocol->setNoValidateCert((bool)$params->novalidatecert);
}

$this->protocol->connect($host, $port, $ssl);
$this->protocol->login($params->user, $password);
}
Expand Down
11 changes: 11 additions & 0 deletions test/Storage/ImapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ public function testConnectTLS()
new Storage\Imap($this->params);
}

public function testConnectSelfSignedSSL()
{
if (! getenv('TESTS_LAMINAS_MAIL_IMAP_SSL')) {
return;
}

$this->params['ssl'] = 'SSL';
$this->params['novalidatecert'] = true;
new Storage\Imap($this->params);
}

public function testInvalidService()
{
$this->params['port'] = getenv('TESTS_LAMINAS_MAIL_IMAP_INVALID_PORT');
Expand Down
12 changes: 12 additions & 0 deletions test/Storage/Pop3Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ public function testConnectTLS()
new Storage\Pop3($this->params);
}

public function testConnectSelfSignedSSL()
{
if (! getenv('TESTS_LAMINAS_MAIL_POP3_SSL')) {
return;
}

$this->params['ssl'] = 'SSL';
$this->params['novalidatecert'] = true;

new Storage\Pop3($this->params);
}

public function testInvalidService()
{
$this->params['port'] = getenv('TESTS_LAMINAS_MAIL_POP3_INVALID_PORT');
Expand Down