1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
mailer.php
См. документацию.
1<?php
8
9namespace Bitrix\Main\Mail\Smtp;
10
11use Bitrix\Main\Error;
12use Bitrix\Main\HttpApplication;
13use Bitrix\Main\Mail\Context;
14use Bitrix\Main\Localization\Loc;
15use Bitrix\Main\Mail\Sender;
16use Bitrix\Main\Mail\SenderSendCounter;
17use PHPMailer\PHPMailer\PHPMailer;
18use Bitrix\Main\Diag\FileLogger;
19
20class Mailer extends PHPMailer
21{
25 private static $instances = [];
26 public const KEEP_ALIVE_ALWAYS = 'keep_alive_always';
27 public const KEEP_ALIVE_NONE = 'keep_alive_none';
28 public const KEEP_ALIVE_OPTIONAL = 'keep_alive_optional';
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';
32
33 private $configuration;
34
36 {
37 $configuration = \Bitrix\Main\Config\Configuration::getValue('smtp');
38 if ($context->getSmtp())
39 {
40 return [
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(),
50 ];
51 }
52
53 return $configuration ?? [];
54 }
55
56
62 public function prepareConfiguration(Context $context): bool
63 {
64 $this->configuration = $this->getActualConfiguration($context);
65
66 $this->SMTPDebug = $this->configuration['debug'] ?? false;
67
68 if ($this->SMTPDebug)
69 {
70 $configuration = $this->configuration;
71 $this->Debugoutput = function ($logMessage) use ($configuration) {
72 $logger = new FileLogger(
73 $configuration['logFile'] ?? (($_SERVER['DOCUMENT_ROOT'] ?? __DIR__) . '/mailer.log')
74 );
75 $logger->info($logMessage);
76 };
77 }
78 $this->isSMTP();
79
80 $this->SMTPAuth = (bool)$this->configuration['password'];
81 $this->prepareOauthConfiguration();
82
83 if (
84 !$this->configuration['host']
85 || !$this->configuration['login']
86 )
87 {
88 return false;
89 }
90
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;
96
97 if (
98 'smtps' === $this->configuration['encryption_type']
99 || ('smtp' !== $this->configuration['encryption_type'] && 465 === $this->port))
100 {
101 $this->SMTPSecure = $this->Port == 465 ? PHPMailer::ENCRYPTION_SMTPS : PHPMailer::ENCRYPTION_STARTTLS;
102 }
103
104 $this->Timeout = $this->configuration['connection_timeout'] ?? 30;
105
106 return true;
107 }
108
114 private function prepareOauthConfiguration(): void
115 {
116 if (!empty($this->configuration['isOauth']))
117 {
118 $this->AuthType = 'XOAUTH2';
119 $this->setOAuth(new PreparedOauthCredentials([
120 'userName' => $this->configuration['login'],
121 'token' => $this->configuration['password'],
122 ]));
123 }
124 }
125
130 public function setMIMEBody($body)
131 {
132 $this->MIMEBody = $body;
133 }
134
139 public function setMIMEHeader($headers)
140 {
141 $this->MIMEHeader = $headers;
142 }
143
144
155 public function sendMailBySmtp(
156 string $sourceTo,
157 string $subject,
158 string $message,
159 string $additional_headers,
160 string $additional_parameters
161 ): bool
162 {
163
164 $addresses = Mailer::parseAddresses($sourceTo)??[];
165 if (empty($addresses))
166 {
167 return false;
168 }
169
171
172 if ($subject && $additional_headers)
173 {
174 $this->Subject = $subject;
175 $additional_headers .= $eol . 'Subject: ' . $subject;
176 }
177
178 preg_match(self::HEADER_FROM_REGEX, $additional_headers, $headerFrom);;
179 if ($this->configuration['force_from'] && $headerFrom)
180 {
181 $additional_headers = preg_replace(
182 self::HEADER_FROM_REGEX,
183 $this->configuration['from'],
184 $additional_headers
185 );
186 }
187
188 if (!$headerFrom)
189 {
190 $additional_headers .= $eol . 'From: ' . $this->configuration['from'];
191 }
192
193 $this->clearAllRecipients();
194 foreach ($addresses as $to)
195 {
196 if (!$to['address'])
197 {
198 continue;
199 }
200 $this->addAddress($to['address'], $to['name']);
201 }
202
203 $additional_headers .= $eol . 'To: ' . $sourceTo;
204
205 $this->prepareBCCRecipients($additional_headers);
206 $this->prepareCCRecipients($additional_headers);
207
208 $this->setMIMEBody($message);
209 $this->setMIMEHeader($additional_headers);
210
211 $canSend = $this->checkLimit();
212 if (!$canSend)
213 {
214 return false;
215 }
216
217 $sendResult = $this->postSend();
218
219 if ($sendResult)
220 {
221 $this->increaseLimit();
222 }
223
224 return $sendResult;
225 }
226
227 private function prepareCCRecipients(&$additional_headers): void
228 {
229 preg_match(self::HEADER_CC_REGEX, $additional_headers, $matches);
230
231 if ($matches)
232 {
233 $recipients = explode(',', trim($matches['emails']));
234
235 foreach ($recipients as $to)
236 {
237 $to = self::parseAddresses($to) ?? [];
238 if (!$to)
239 {
240 continue;
241 }
242 $this->addCC($to[0]['address'], $to[0]['name']);
243 }
244 }
245 }
246
247 private function prepareBCCRecipients(&$additional_headers): void
248 {
249 preg_match(self::HEADER_BCC_REGEX, $additional_headers, $matches);
250
251 if ($matches)
252 {
253 $recipients = explode(',', trim($matches['emails']));
254
255 foreach ($recipients as $to)
256 {
257 $to = self::parseAddresses($to) ?? [];
258 if (!$to)
259 {
260 continue;
261 }
262 $this->addBCC($to[0]['address'], $to[0]['name']);
263 }
264 }
265
266 $additional_headers = $this->removeHeader('bcc', $additional_headers);
267 }
268
269 private function removeHeader(string $headerName, $additional_headers): array|string|null
270 {
271 $headerRegex = '/^\s*' . preg_quote($headerName, '/') . ':(.*)\R?/im';
272
273 return preg_replace($headerRegex, '', $additional_headers);
274 }
275
283 public static function getInstance(Context $context): ?Mailer
284 {
285 $key = hash('sha256', serialize($context));
286 if (!static::$instances[$key])
287 {
288 $mail = new Mailer();
289 if (!$mail->prepareConfiguration($context))
290 {
291 return null;
292 }
293
294 if ($context->getSmtp())
295 {
296 $mail->setFrom($context->getSmtp()->getFrom());
297 }
298
299 switch ($context->getKeepAlive())
300 {
301 default:
302 case self::KEEP_ALIVE_NONE:
303 $mail->SMTPKeepAlive = false;
304 break;
305 case self::KEEP_ALIVE_ALWAYS:
306 $mail->SMTPKeepAlive = true;
307 HttpApplication::getInstance()->addBackgroundJob(
308 function () use ($mail)
309 {
310 $mail->smtpClose();
311 });
312 break;
313 case self::KEEP_ALIVE_OPTIONAL:
314 $mail->SMTPKeepAlive = true;
315 break;
316 }
317
318 static::$instances[$key] = $mail;
319 }
320
321 return static::$instances[$key];
322 }
323
333 {
334 $mail = new Mailer();
335
337 {
338 // Private addresses can't be used in the cloud
339 $ip = \Bitrix\Main\Web\IpAddress::createByName($context->getSmtp()->getHost());
340 if ($ip->isPrivate())
341 {
342 $errors->setError(new Error('SMTP server address is invalid'));
343 return false;
344 }
345 }
346
347 if (!$mail->prepareConfiguration($context))
348 {
349 return false;
350 }
351
352 if ($mail->smtpConnect())
353 {
354 $mail->smtpClose();
355 return true;
356 }
357
358 $errors->setError(new Error(Loc::getMessage('main_mail_smtp_connection_failed')));
359 return false;
360 }
361
362 private function checkLimit(): bool
363 {
364 $from = self::parseAddresses($this->From)[0]['address'];
365 $count = count($this->getAllRecipientAddresses());
366
367 $emailCounter = new SenderSendCounter();
368 $emailDailyLimit = Sender::getEmailLimit($from);
369 if($emailDailyLimit
370 && ($emailCounter->get($from) + $count) > $emailDailyLimit)
371 {
372 //daily limit exceeded
373 return false;
374 }
375
376 return true;
377 }
378
379 private function increaseLimit()
380 {
381 $from = self::parseAddresses($this->From)[0]['address'];
382 $emailDailyLimit = Sender::getEmailLimit($from);
383
384 if (!$emailDailyLimit)
385 {
386 return;
387 }
388
389 $emailCounter = new SenderSendCounter();
390 $count = count($this->getAllRecipientAddresses());
391
392 $emailCounter->increment($from, $count);
393 }
394
398 public static function closeConnections(): void
399 {
400 foreach (static::$instances as $instance)
401 {
402 $instance->smtpClose();
403 }
404 }
405
406}
$count
Определения admin_tab.php:4
static getInstance()
Определения application.php:98
static getValue($name)
Определения configuration.php:24
Определения error.php:15
static getMailEol()
Определения mail.php:856
const KEEP_ALIVE_ALWAYS
Определения mailer.php:26
setMIMEBody($body)
Определения mailer.php:130
const KEEP_ALIVE_OPTIONAL
Определения mailer.php:28
static getInstance(Context $context)
Определения mailer.php:283
static checkConnect(Context $context, \Bitrix\Main\ErrorCollection $errors)
Определения mailer.php:332
static closeConnections()
Определения mailer.php:398
getActualConfiguration(Context $context)
Определения mailer.php:35
const KEEP_ALIVE_NONE
Определения mailer.php:27
prepareConfiguration(Context $context)
Определения mailer.php:62
sendMailBySmtp(string $sourceTo, string $subject, string $message, string $additional_headers, string $additional_parameters)
Определения mailer.php:155
setMIMEHeader($headers)
Определения mailer.php:139
static isModuleInstalled($moduleName)
Определения modulemanager.php:125
static createByName($name)
Определения ipaddress.php:32
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$errors
Определения iblock_catalog_edit.php:74
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
$context
Определения csv_new_setup.php:223
Определения culture.php:9
$message
Определения payment.php:8
$instance
Определения ps_b24_final.php:14
if(empty($signedUserToken)) $key
Определения quickway.php:257
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$matches
Определения index.php:22