Improved NetClient connection handling as discovered at #381

This commit is contained in:
the-djmaze 2022-05-12 11:59:23 +02:00
parent 7e30cb95ec
commit 426a0f8c3b
9 changed files with 147 additions and 104 deletions

View file

@ -84,14 +84,11 @@ class ImapClient extends \MailSo\Net\NetClient
* @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Net\Exceptions\Exception
* @throws \MailSo\Imap\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception
*/ */
public function Connect(string $sServerName, int $iPort = 143, public function Connect(\MailSo\Net\ConnectSettings $oSettings) : void
int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT,
bool $bVerifySsl = false, bool $bAllowSelfSigned = true,
string $sClientCert = '') : void
{ {
$this->aTagTimeouts['*'] = \microtime(true); $this->aTagTimeouts['*'] = \microtime(true);
parent::Connect($sServerName, $iPort, $iSecurityType, $bVerifySsl, $bAllowSelfSigned, $sClientCert); parent::Connect($oSettings);
$this->setCapabilities($this->getResponse('*')); $this->setCapabilities($this->getResponse('*'));

View file

@ -0,0 +1,65 @@
<?php
/*
* This file is part of MailSo.
*
* (c) 2022 DJMaze
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MailSo\Net;
/**
* @category MailSo
* @package Net
*/
class ConnectSettings
{
public
$host,
$port,
// none, TLS, STARTTLS
$type = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT,
// https://www.php.net/context.ssl
$ssl = [
// 'peer_name' => '',
// 'peer_fingerprint' => '', // string | array
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
// 'cafile' => '',
// 'capath' => '',
// 'ciphers' => 'HIGH:!SSLv2:!SSLv3',
'SNI_enabled' => true,
'disable_compression' => true,
'security_level' => 1,
// 'local_cert' => '',
// 'local_pk' => '',
// 'passphrase' => '',
// 'verify_depth' => 0,
// 'capture_peer_cert' => false,
// 'capture_peer_cert_chain' => false,
];
function __construct()
{
// TODO: This should be moved to \RainLoop\Model\Domain
$oConfig = \RainLoop\API::Config();
$this->ssl['verify_peer'] = !!$oConfig->Get('ssl', 'verify_certificate', false);
$this->ssl['verify_peer_name'] = !!$oConfig->Get('ssl', 'verify_certificate', false);
$this->ssl['allow_self_signed'] = !!$oConfig->Get('ssl', 'allow_self_signed', true);
$this->ssl['security_level'] = (int) $oConfig->Get('ssl', 'security_level', 1);
// $this->ssl['local_cert'] = (string) $oConfig->Get('ssl', 'client_cert', '');
// $this->ssl['cafile'] = (string) $oConfig->Get('ssl', 'cafile', '');
// $this->ssl['capath'] = (string) $oConfig->Get('ssl', 'capath', '');
}
}

View file

@ -120,13 +120,10 @@ abstract class NetClient
* @throws \MailSo\Net\Exceptions\SocketAlreadyConnectedException * @throws \MailSo\Net\Exceptions\SocketAlreadyConnectedException
* @throws \MailSo\Net\Exceptions\SocketCanNotConnectToHostException * @throws \MailSo\Net\Exceptions\SocketCanNotConnectToHostException
*/ */
public function Connect(string $sServerName, int $iPort, public function Connect(ConnectSettings $oSettings)
int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT,
bool $bVerifySsl = false, bool $bAllowSelfSigned = true,
string $sClientCert = '') : void
{ {
if (!\strlen(\trim($sServerName)) || !\MailSo\Base\Validator::PortInt($iPort)) $oSettings->host = \trim($oSettings->host);
{ if (!\strlen($oSettings->host) || !\MailSo\Base\Validator::PortInt($oSettings->port)) {
$this->writeLogException( $this->writeLogException(
new \MailSo\Base\Exceptions\InvalidArgumentException, new \MailSo\Base\Exceptions\InvalidArgumentException,
\MailSo\Log\Enumerations\Type::ERROR, true); \MailSo\Log\Enumerations\Type::ERROR, true);
@ -139,14 +136,12 @@ abstract class NetClient
\MailSo\Log\Enumerations\Type::ERROR, true); \MailSo\Log\Enumerations\Type::ERROR, true);
} }
$sServerName = \trim($sServerName);
$sErrorStr = ''; $sErrorStr = '';
$iErrorNo = 0; $iErrorNo = 0;
$this->sConnectedHost = $sServerName; $this->sConnectedHost = $oSettings->host;
$this->iConnectedPort = $iPort; $this->iConnectedPort = $oSettings->port;
$this->iSecurityType = $iSecurityType; $this->iSecurityType = $oSettings->type;
$this->bSecure = \MailSo\Net\Enumerations\ConnectionSecurityType::UseSSL( $this->bSecure = \MailSo\Net\Enumerations\ConnectionSecurityType::UseSSL(
$this->iConnectedPort, $this->iSecurityType); $this->iConnectedPort, $this->iSecurityType);
@ -168,23 +163,9 @@ abstract class NetClient
\MailSo\Log\Enumerations\Type::NOTE); \MailSo\Log\Enumerations\Type::NOTE);
$aStreamContextSettings = array( $aStreamContextSettings = array(
'ssl' => array( 'ssl' => $oSettings->ssl
'verify_host' => $bVerifySsl,
'verify_peer' => $bVerifySsl,
'verify_peer_name' => $bVerifySsl,
'allow_self_signed' => $bVerifySsl ? $bAllowSelfSigned : true,
// 'ciphers' => 'HIGH:!SSLv2:!SSLv3',
'SNI_enabled' => true,
// 'disable_compression' => true,
'security_level' => 1
)
); );
if (!empty($sClientCert))
{
$aStreamContextSettings['ssl']['local_cert'] = $sClientCert;
}
\MailSo\Hooks::Run('Net.NetClient.StreamContextSettings/Filter', array(&$aStreamContextSettings)); \MailSo\Hooks::Run('Net.NetClient.StreamContextSettings/Filter', array(&$aStreamContextSettings));
$rStreamContext = \stream_context_create($aStreamContextSettings); $rStreamContext = \stream_context_create($aStreamContextSettings);
@ -228,7 +209,7 @@ abstract class NetClient
} }
} }
public function EnableCrypto(bool $insecure = true) public function EnableCrypto(bool $insecure = false)
{ {
$bError = true; $bError = true;
if ($this->rConnect && \MailSo\Base\Utils::FunctionExistsAndEnabled('stream_socket_enable_crypto')) { if ($this->rConnect && \MailSo\Base\Utils::FunctionExistsAndEnabled('stream_socket_enable_crypto')) {

View file

@ -62,11 +62,9 @@ class ManageSieveClient extends \MailSo\Net\NetClient
* @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Base\Exceptions\InvalidArgumentException
* @throws \MailSo\Sieve\Exceptions\ResponseException * @throws \MailSo\Sieve\Exceptions\ResponseException
*/ */
public function Connect(string $sServerName, int $iPort, public function Connect(\MailSo\Net\ConnectSettings $oSettings) : void
int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT,
bool $bVerifySsl = false, bool $bAllowSelfSigned = true, string $sClientCert = '') : void
{ {
parent::Connect($sServerName, $iPort, $iSecurityType, $bVerifySsl, $bAllowSelfSigned); parent::Connect($oSettings);
$aResponse = $this->parseResponse(); $aResponse = $this->parseResponse();
$this->validateResponse($aResponse); $this->validateResponse($aResponse);

View file

@ -100,12 +100,9 @@ class SmtpClient extends \MailSo\Net\NetClient
* @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Net\Exceptions\Exception
* @throws \MailSo\Smtp\Exceptions\ResponseException * @throws \MailSo\Smtp\Exceptions\ResponseException
*/ */
public function Connect(string $sServerName, int $iPort = 25, public function Connect(\MailSo\Net\ConnectSettings $oSettings, string $sEhloHost = '[127.0.0.1]') : void
int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT,
bool $bVerifySsl = false, bool $bAllowSelfSigned = true,
string $sClientCert = '', string $sEhloHost = '[127.0.0.1]') : void
{ {
parent::Connect($sServerName, $iPort, $iSecurityType, $bVerifySsl, $bAllowSelfSigned); parent::Connect($oSettings);
$this->validateResponse(220); $this->validateResponse(220);

View file

@ -112,11 +112,10 @@ trait UserAuth
$this->Logger()->AddSecret($sPassword); $this->Logger()->AddSecret($sPassword);
$oAccount = null; $oAccount = null;
$sClientCert = \trim($this->Config()->Get('ssl', 'client_cert', ''));
try { try {
$oAccount = $bMainAccount $oAccount = $bMainAccount
? MainAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, $sClientCert, true) ? MainAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, true)
: AdditionalAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, $sClientCert, true); : AdditionalAccount::NewInstanceFromCredentials($this, $sEmail, $sLogin, $sPassword, true);
if (!$oAccount) { if (!$oAccount) {
throw new ClientException(Notifications::AuthError); throw new ClientException(Notifications::AuthError);
} }

View file

@ -295,11 +295,11 @@ class ActionsAdmin extends Actions
$oImapClient->SetTimeOuts($iConnectionTimeout); $oImapClient->SetTimeOuts($iConnectionTimeout);
$iTime = \microtime(true); $iTime = \microtime(true);
$oImapClient->Connect($oDomain->IncHost(), $oDomain->IncPort(), $oDomain->IncSecure(), $oSettings = new \MailSo\Net\ConnectSettings;
!!$this->Config()->Get('ssl', 'verify_certificate', false), $oSettings->host = $oDomain->IncHost();
!!$this->Config()->Get('ssl', 'allow_self_signed', true), $oSettings->port = $oDomain->IncPort();
$this->Config()->Get('ssl', 'client_cert', '') $oSettings->type = $oDomain->IncSecure();
); $oImapClient->Connect($oSettings);
$oImapClient->Disconnect(); $oImapClient->Disconnect();
$bImapResult = true; $bImapResult = true;
@ -336,11 +336,11 @@ class ActionsAdmin extends Actions
$oSmtpClient->SetTimeOuts($iConnectionTimeout); $oSmtpClient->SetTimeOuts($iConnectionTimeout);
$iTime = \microtime(true); $iTime = \microtime(true);
$oSmtpClient->Connect($oDomain->OutHost(), $oDomain->OutPort(), $oDomain->OutSecure(), $oSettings = new \MailSo\Net\ConnectSettings;
!!$this->Config()->Get('ssl', 'verify_certificate', false), $oSettings->host = $oDomain->OutHost();
!!$this->Config()->Get('ssl', 'allow_self_signed', true), $oSettings->port = $oDomain->OutPort();
'', \MailSo\Smtp\SmtpClient::EhloHelper() $oSettings->type = $oDomain->OutSecure();
); $oSmtpClient->Connect($oSettings, \MailSo\Smtp\SmtpClient::EhloHelper());
$oSmtpClient->Disconnect(); $oSmtpClient->Disconnect();
$bSmtpResult = true; $bSmtpResult = true;
@ -370,10 +370,11 @@ class ActionsAdmin extends Actions
$oSieveClient->SetTimeOuts($iConnectionTimeout); $oSieveClient->SetTimeOuts($iConnectionTimeout);
$iTime = \microtime(true); $iTime = \microtime(true);
$oSieveClient->Connect($oDomain->SieveHost(), $oDomain->SievePort(), $oDomain->SieveSecure(), $oSettings = new \MailSo\Net\ConnectSettings;
!!$this->Config()->Get('ssl', 'verify_certificate', false), $oSettings->host = $oDomain->SieveHost();
!!$this->Config()->Get('ssl', 'allow_self_signed', true) $oSettings->port = $oDomain->SievePort();
); $oSettings->type = $oDomain->SieveSecure();
$oSieveClient->Connect($oSettings);
$oSieveClient->Disconnect(); $oSieveClient->Disconnect();
$bSieveResult = true; $bSieveResult = true;

View file

@ -201,6 +201,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'ssl' => array( 'ssl' => array(
'verify_certificate' => array(false, 'Require verification of SSL certificate used.'), 'verify_certificate' => array(false, 'Require verification of SSL certificate used.'),
'allow_self_signed' => array(true, 'Allow self-signed certificates. Requires verify_certificate.'), 'allow_self_signed' => array(true, 'Allow self-signed certificates. Requires verify_certificate.'),
'security_level' => array(1, 'https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html'),
'cafile' => array('', 'Location of Certificate Authority file on local filesystem (/etc/ssl/certs/ca-certificates.crt)'), 'cafile' => array('', 'Location of Certificate Authority file on local filesystem (/etc/ssl/certs/ca-certificates.crt)'),
'capath' => array('', 'capath must be a correctly hashed certificate directory. (/etc/ssl/certs/)'), 'capath' => array('', 'capath must be a correctly hashed certificate directory. (/etc/ssl/certs/)'),
'client_cert' => array('', 'Location of client certificate file (pem format with private key) on local filesystem'), 'client_cert' => array('', 'Location of client certificate file (pem format with private key) on local filesystem'),

View file

@ -33,11 +33,6 @@ abstract class Account implements \JsonSerializable
*/ */
private $sProxyAuthPassword = ''; private $sProxyAuthPassword = '';
/**
* @var string
*/
private $sClientCert;
/** /**
* @var \RainLoop\Model\Domain * @var \RainLoop\Model\Domain
*/ */
@ -95,11 +90,6 @@ abstract class Account implements \JsonSerializable
return $this->IncPassword(); return $this->IncPassword();
} }
public function ClientCert() : string
{
return $this->sClientCert;
}
public function Domain() : Domain public function Domain() : Domain
{ {
return $this->oDomain; return $this->oDomain;
@ -138,15 +128,14 @@ abstract class Account implements \JsonSerializable
$this->sEmail, // 1 $this->sEmail, // 1
$this->sLogin, // 2 $this->sLogin, // 2
$this->sPassword, // 3 $this->sPassword, // 3
$this->sClientCert, // 4 '', // 4 sClientCert
$this->sProxyAuthUser, // 5 $this->sProxyAuthUser, // 5
$this->sProxyAuthPassword // 6 $this->sProxyAuthPassword // 6
); );
} }
public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions, public static function NewInstanceFromCredentials(\RainLoop\Actions $oActions,
string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', string $sEmail, string $sLogin, string $sPassword, bool $bThrowException = false): ?self
bool $bThrowException = false): ?self
{ {
$oAccount = null; $oAccount = null;
if ($sEmail && $sLogin && $sPassword) { if ($sEmail && $sLogin && $sPassword) {
@ -159,7 +148,6 @@ abstract class Account implements \JsonSerializable
$oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin); $oAccount->sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin);
$oAccount->sPassword = $sPassword; $oAccount->sPassword = $sPassword;
$oAccount->oDomain = $oDomain; $oAccount->oDomain = $oDomain;
$oAccount->sClientCert = $sClientCert;
$oActions->Plugins()->RunHook('filter.account', array($oAccount)); $oActions->Plugins()->RunHook('filter.account', array($oAccount));
@ -188,7 +176,6 @@ abstract class Account implements \JsonSerializable
$aAccountHash[1] ?: '', $aAccountHash[1] ?: '',
$aAccountHash[2] ?: '', $aAccountHash[2] ?: '',
$aAccountHash[3] ?: '', $aAccountHash[3] ?: '',
$aAccountHash[4] ?: '',
$bThrowExceptionOnFalse $bThrowExceptionOnFalse
); );
@ -212,31 +199,38 @@ abstract class Account implements \JsonSerializable
{ {
$oImapClient = $oMailClient->ImapClient(); $oImapClient = $oMailClient->ImapClient();
$aImapCredentials = \array_merge( $aCredentials = \array_merge(
$this->Domain()->ImapSettings(), $this->Domain()->ImapSettings(),
array( array(
'Login' => $this->IncLogin(), 'Login' => $this->IncLogin(),
'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false), 'VerifySsl' => !!$oConfig->Get('ssl', 'verify_certificate', false),
'ClientCert' => $this->ClientCert(), 'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true),
'AllowSelfSigned' => !!$oConfig->Get('ssl', 'allow_self_signed', true) 'ClientCert' => \trim($oConfig->Get('ssl', 'client_cert', ''))
) )
); );
$oPlugins->RunHook('imap.before-connect', array($this, $oImapClient, &$aImapCredentials)); $oPlugins->RunHook('imap.before-connect', array($this, $oImapClient, &$aCredentials));
if ($aImapCredentials['UseConnect']) { if ($aCredentials['UseConnect']) {
$oImapClient->Connect($aImapCredentials['Host'], $aImapCredentials['Port'], $oSettings = new \MailSo\Net\ConnectSettings;
$aImapCredentials['Secure'], $aImapCredentials['VerifySsl'], $oSettings->host = $aCredentials['Host'];
$aImapCredentials['AllowSelfSigned'], $aImapCredentials['ClientCert']); $oSettings->port = $aCredentials['Port'];
$oSettings->type = $aCredentials['Secure'];
$oSettings->ssl['verify_peer'] = !!$aCredentials['VerifySsl'];
$oSettings->ssl['verify_peer_name'] = !!$aCredentials['VerifySsl'];
$oSettings->ssl['allow_self_signed'] = !!$aCredentials['AllowSelfSigned'];
if ($aCredentials['ClientCert']) {
$oSettings->ssl['local_cert'] = $aCredentials['ClientCert'];
}
$oImapClient->Connect($oSettings);
} }
$oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $aImapCredentials)); $oPlugins->RunHook('imap.after-connect', array($this, $oImapClient, $aCredentials));
return $this->netClientLogin($oImapClient, $oConfig, $oPlugins, $aImapCredentials); return $this->netClientLogin($oImapClient, $oConfig, $oPlugins, $aCredentials);
} }
public function OutConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient, \RainLoop\Config\Application $oConfig, bool &$bUsePhpMail = false) : bool public function OutConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Smtp\SmtpClient $oSmtpClient, \RainLoop\Config\Application $oConfig, bool &$bUsePhpMail = false) : bool
{ {
$aSmtpCredentials = \array_merge( $aCredentials = \array_merge(
$this->Domain()->SmtpSettings(), $this->Domain()->SmtpSettings(),
array( array(
'UseConnect' => !$bUsePhpMail, 'UseConnect' => !$bUsePhpMail,
@ -247,23 +241,28 @@ abstract class Account implements \JsonSerializable
) )
); );
$oPlugins->RunHook('smtp.before-connect', array($this, $oSmtpClient, &$aSmtpCredentials)); $oPlugins->RunHook('smtp.before-connect', array($this, $oSmtpClient, &$aCredentials));
$bUsePhpMail = $aSmtpCredentials['UsePhpMail']; $bUsePhpMail = $aCredentials['UsePhpMail'];
$aSmtpCredentials['UseAuth'] = $aSmtpCredentials['UseAuth'] && !$aSmtpCredentials['UsePhpMail']; $aCredentials['UseAuth'] = $aCredentials['UseAuth'] && !$aCredentials['UsePhpMail'];
if ($aSmtpCredentials['UseConnect'] && !$aSmtpCredentials['UsePhpMail']) {
$oSmtpClient->Connect($aSmtpCredentials['Host'], $aSmtpCredentials['Port'],
$aSmtpCredentials['Secure'], $aSmtpCredentials['VerifySsl'], $aSmtpCredentials['AllowSelfSigned'],
'', $aSmtpCredentials['Ehlo']
);
}
$oPlugins->RunHook('smtp.after-connect', array($this, $oSmtpClient, $aSmtpCredentials));
return $this->netClientLogin($oSmtpClient, $oConfig, $oPlugins, $aSmtpCredentials); if ($aCredentials['UseConnect'] && !$aCredentials['UsePhpMail']) {
$oSettings = new \MailSo\Net\ConnectSettings;
$oSettings->host = $aCredentials['Host'];
$oSettings->port = $aCredentials['Port'];
$oSettings->type = $aCredentials['Secure'];
$oSettings->ssl['verify_peer'] = !!$aCredentials['VerifySsl'];
$oSettings->ssl['verify_peer_name'] = !!$aCredentials['VerifySsl'];
$oSettings->ssl['allow_self_signed'] = !!$aCredentials['AllowSelfSigned'];
$oSmtpClient->Connect($oSettings, $aCredentials['Ehlo']);
}
$oPlugins->RunHook('smtp.after-connect', array($this, $oSmtpClient, $aCredentials));
return $this->netClientLogin($oSmtpClient, $oConfig, $oPlugins, $aCredentials);
} }
public function SieveConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\ManageSieveClient $oSieveClient, \RainLoop\Config\Application $oConfig) public function SieveConnectAndLoginHelper(\RainLoop\Plugins\Manager $oPlugins, \MailSo\Sieve\ManageSieveClient $oSieveClient, \RainLoop\Config\Application $oConfig)
{ {
$aSieveCredentials = \array_merge( $aCredentials = \array_merge(
$this->Domain()->SieveSettings(), $this->Domain()->SieveSettings(),
array( array(
'Login' => $this->IncLogin(), 'Login' => $this->IncLogin(),
@ -273,15 +272,20 @@ abstract class Account implements \JsonSerializable
) )
); );
$oPlugins->RunHook('sieve.before-connect', array($this, $oSieveClient, &$aSieveCredentials)); $oPlugins->RunHook('sieve.before-connect', array($this, $oSieveClient, &$aCredentials));
if ($aSieveCredentials['UseConnect']) { if ($aCredentials['UseConnect']) {
$oSieveClient->Connect($aSieveCredentials['Host'], $aSieveCredentials['Port'], $oSettings = new \MailSo\Net\ConnectSettings;
$aSieveCredentials['Secure'], $aSieveCredentials['VerifySsl'], $aSieveCredentials['AllowSelfSigned'] $oSettings->host = $aCredentials['Host'];
); $oSettings->port = $aCredentials['Port'];
$oSettings->type = $aCredentials['Secure'];
$oSettings->ssl['verify_peer'] = !!$aCredentials['VerifySsl'];
$oSettings->ssl['verify_peer_name'] = !!$aCredentials['VerifySsl'];
$oSettings->ssl['allow_self_signed'] = !!$aCredentials['AllowSelfSigned'];
$oSieveClient->Connect($oSettings);
} }
$oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $aSieveCredentials)); $oPlugins->RunHook('sieve.after-connect', array($this, $oSieveClient, $aCredentials));
return $this->netClientLogin($oSieveClient, $oConfig, $oPlugins, $aSieveCredentials); return $this->netClientLogin($oSieveClient, $oConfig, $oPlugins, $aCredentials);
} }
private function netClientLogin(\MailSo\Net\NetClient $oClient, \RainLoop\Config\Application $oConfig, \RainLoop\Plugins\Manager $oPlugins, array $aCredentials) : bool private function netClientLogin(\MailSo\Net\NetClient $oClient, \RainLoop\Config\Application $oConfig, \RainLoop\Plugins\Manager $oPlugins, array $aCredentials) : bool