25 private static $instances = [];
29 private const HEADER_FROM_REGEX =
'/(?<=From:).*?(?=\\n)/iu';
30 private const HEADER_CC_REGEX =
'/^\s*cc:(?<emails>.+)/im';
31 private const HEADER_BCC_REGEX =
'/^\s*bcc:(?<emails>.+)/im';
33 private $configuration;
41 'host' =>
$context->getSmtp()->getHost(),
42 'port' =>
$context->getSmtp()->getPort(),
43 'encryption_type' =>
$context->getSmtp()->getProtocol() ??
'smtp',
44 'login' =>
$context->getSmtp()->getLogin(),
45 'password' =>
$context->getSmtp()->getPassword(),
46 'from' =>
$context->getSmtp()->getFrom(),
47 'debug' => $configuration[
'debug'] ??
false,
48 'logFile' => $configuration[
'log_file'] ??
null,
49 'isOauth' =>
$context->getSmtp()->getIsOauth(),
53 return $configuration ?? [];
66 $this->SMTPDebug = $this->configuration[
'debug'] ??
false;
70 $configuration = $this->configuration;
71 $this->Debugoutput =
function ($logMessage) use ($configuration) {
73 $configuration[
'logFile'] ?? ((
$_SERVER[
'DOCUMENT_ROOT'] ?? __DIR__) .
'/mailer.log')
75 $logger->info($logMessage);
80 $this->SMTPAuth = (bool)$this->configuration[
'password'];
81 $this->prepareOauthConfiguration();
84 !$this->configuration[
'host']
85 || !$this->configuration[
'login']
91 $this->From = $this->configuration[
'from'];
92 $this->Host = $this->configuration[
'host'];
93 $this->Username = $this->configuration[
'login'];
94 $this->
Password = $this->configuration[
'password'] ??
'';
95 $this->Port = $this->configuration[
'port'] ?? 465;
98 'smtps' === $this->configuration[
'encryption_type']
99 || (
'smtp' !== $this->configuration[
'encryption_type'] && 465 === $this->port))
101 $this->SMTPSecure = $this->Port == 465 ? PHPMailer::ENCRYPTION_SMTPS : PHPMailer::ENCRYPTION_STARTTLS;
104 $this->Timeout = $this->configuration[
'connection_timeout'] ?? 30;
114 private function prepareOauthConfiguration(): void
116 if (!empty($this->configuration[
'isOauth']))
118 $this->AuthType =
'XOAUTH2';
120 'userName' => $this->configuration[
'login'],
121 'token' => $this->configuration[
'password'],
132 $this->MIMEBody = $body;
141 $this->MIMEHeader = $headers;
159 string $additional_headers,
160 string $additional_parameters
164 $addresses = Mailer::parseAddresses($sourceTo)??[];
165 if (empty($addresses))
172 if ($subject && $additional_headers)
174 $this->Subject = $subject;
175 $additional_headers .= $eol .
'Subject: ' . $subject;
178 preg_match(self::HEADER_FROM_REGEX, $additional_headers, $headerFrom);;
179 if ($this->configuration[
'force_from'] && $headerFrom)
181 $additional_headers = preg_replace(
182 self::HEADER_FROM_REGEX,
183 $this->configuration[
'from'],
190 $additional_headers .= $eol .
'From: ' . $this->configuration[
'from'];
193 $this->clearAllRecipients();
194 foreach ($addresses as $to)
200 $this->addAddress($to[
'address'], $to[
'name']);
203 $additional_headers .= $eol .
'To: ' . $sourceTo;
205 $this->prepareBCCRecipients($additional_headers);
206 $this->prepareCCRecipients($additional_headers);
211 $canSend = $this->checkLimit();
217 $sendResult = $this->postSend();
221 $this->increaseLimit();
227 private function prepareCCRecipients(&$additional_headers): void
229 preg_match(self::HEADER_CC_REGEX, $additional_headers,
$matches);
233 $recipients = explode(
',', trim(
$matches[
'emails']));
235 foreach ($recipients as $to)
237 $to = self::parseAddresses($to) ?? [];
242 $this->addCC($to[0][
'address'], $to[0][
'name']);
247 private function prepareBCCRecipients(&$additional_headers): void
249 preg_match(self::HEADER_BCC_REGEX, $additional_headers,
$matches);
253 $recipients = explode(
',', trim(
$matches[
'emails']));
255 foreach ($recipients as $to)
257 $to = self::parseAddresses($to) ?? [];
262 $this->addBCC($to[0][
'address'], $to[0][
'name']);
266 $additional_headers = $this->removeHeader(
'bcc', $additional_headers);
269 private function removeHeader(
string $headerName, $additional_headers):
array|string|null
271 $headerRegex =
'/^\s*' . preg_quote($headerName,
'/') .
':(.*)\R?/im';
273 return preg_replace($headerRegex,
'', $additional_headers);
286 if (!static::$instances[
$key])
289 if (!$mail->prepareConfiguration(
$context))
296 $mail->setFrom(
$context->getSmtp()->getFrom());
302 case self::KEEP_ALIVE_NONE:
303 $mail->SMTPKeepAlive =
false;
305 case self::KEEP_ALIVE_ALWAYS:
306 $mail->SMTPKeepAlive =
true;
308 function () use ($mail)
313 case self::KEEP_ALIVE_OPTIONAL:
314 $mail->SMTPKeepAlive =
true;
318 static::$instances[
$key] = $mail;
321 return static::$instances[
$key];
340 if ($ip->isPrivate())
342 $errors->setError(
new Error(
'SMTP server address is invalid'));
347 if (!$mail->prepareConfiguration(
$context))
352 if ($mail->smtpConnect())
358 $errors->setError(
new Error(Loc::getMessage(
'main_mail_smtp_connection_failed')));
362 private function checkLimit(): bool
364 $from = self::parseAddresses($this->From)[0][
'address'];
368 $emailDailyLimit = Sender::getEmailLimit($from);
370 && ($emailCounter->get($from) +
$count) > $emailDailyLimit)
379 private function increaseLimit()
381 $from = self::parseAddresses($this->From)[0][
'address'];
382 $emailDailyLimit = Sender::getEmailLimit($from);
384 if (!$emailDailyLimit)
389 $emailCounter =
new SenderSendCounter();
392 $emailCounter->increment($from,
$count);
400 foreach (static::$instances as
$instance)
const KEEP_ALIVE_OPTIONAL
static getInstance(Context $context)
static checkConnect(Context $context, \Bitrix\Main\ErrorCollection $errors)
static closeConnections()
getActualConfiguration(Context $context)
prepareConfiguration(Context $context)
sendMailBySmtp(string $sourceTo, string $subject, string $message, string $additional_headers, string $additional_parameters)
static isModuleInstalled($moduleName)
static createByName($name)