Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
mailer.php
1<?php
10
17use PHPMailer\PHPMailer\PHPMailer;
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
35 protected function getActualConfiguration(Context $context): array
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 'force_from' => $configuration['force_from'] ?? false,
49 'logFile' => $configuration['log_file'] ?? null,
50 'isOauth' => $context->getSmtp()->getIsOauth(),
51 ];
52 }
53
54 return $configuration ?? [];
55 }
56
57
63 public function prepareConfiguration(Context $context): bool
64 {
65 $this->configuration = $this->getActualConfiguration($context);
66
67 $this->SMTPDebug = $this->configuration['debug'] ?? false;
68
69 if ($this->SMTPDebug)
70 {
71 $configuration = $this->configuration;
72 $this->Debugoutput = function ($logMessage) use ($configuration) {
73 $logger = new FileLogger(
74 $configuration['logFile'] ?? (($_SERVER['DOCUMENT_ROOT'] ?? __DIR__) . '/mailer.log')
75 );
76 $logger->info($logMessage);
77 };
78 }
79 $this->isSMTP();
80
81 $this->SMTPAuth = (bool)$this->configuration['password'];
82 $this->prepareOauthConfiguration();
83
84 if (
85 !$this->configuration['host']
86 || !$this->configuration['login']
87 )
88 {
89 return false;
90 }
91
92 $this->From = $this->configuration['from'];
93 $this->Host = $this->configuration['host'];
94 $this->Username = $this->configuration['login'];
95 $this->Password = $this->configuration['password'] ?? '';
96 $this->Port = $this->configuration['port'] ?? 465;
97
98 if (
99 'smtps' === $this->configuration['encryption_type']
100 || ('smtp' !== $this->configuration['encryption_type'] && 465 === $this->port))
101 {
102 $this->SMTPSecure = $this->Port == 465 ? PHPMailer::ENCRYPTION_SMTPS : PHPMailer::ENCRYPTION_STARTTLS;
103 }
104
105 $this->Timeout = $this->configuration['connection_timeout'] ?? 30;
106
107 return true;
108 }
109
115 private function prepareOauthConfiguration(): void
116 {
117 if (!empty($this->configuration['isOauth']))
118 {
119 $this->AuthType = 'XOAUTH2';
120 $this->setOAuth(new PreparedOauthCredentials([
121 'userName' => $this->configuration['login'],
122 'token' => $this->configuration['password'],
123 ]));
124 }
125 }
126
131 public function setMIMEBody($body)
132 {
133 $this->MIMEBody = $body;
134 }
135
140 public function setMIMEHeader($headers)
141 {
142 $this->MIMEHeader = $headers;
143 }
144
145
156 public function sendMailBySmtp(
157 string $sourceTo,
158 string $subject,
159 string $message,
160 string $additional_headers,
161 string $additional_parameters
162 ): bool
163 {
164
165 $addresses = Mailer::parseAddresses($sourceTo)??[];
166 if (empty($addresses))
167 {
168 return false;
169 }
170
171 $eol = \Bitrix\Main\Mail\Mail::getMailEol();
172
173 if ($subject && $additional_headers)
174 {
175 $this->Subject = $subject;
176 $additional_headers .= $eol . 'Subject: ' . $subject;
177 }
178
179 preg_match(self::HEADER_FROM_REGEX, $additional_headers, $headerFrom);;
180 if ($this->configuration['force_from'] && $headerFrom)
181 {
182 $additional_headers = preg_replace(
183 self::HEADER_FROM_REGEX,
184 $this->configuration['from'],
185 $additional_headers
186 );
187 }
188
189 if (!$headerFrom)
190 {
191 $additional_headers .= $eol . 'From: ' . $this->configuration['from'];
192 }
193
194 $this->clearAllRecipients();
195 foreach ($addresses as $to)
196 {
197 if (!$to['address'])
198 {
199 continue;
200 }
201 $this->addAddress($to['address'], $to['name']);
202 }
203
204 $additional_headers .= $eol . 'To: ' . $sourceTo;
205
206 $this->prepareBCCRecipients($additional_headers);
207 $this->prepareCCRecipients($additional_headers);
208
209 $this->setMIMEBody($message);
210 $this->setMIMEHeader($additional_headers);
211
212 $canSend = $this->checkLimit();
213 if (!$canSend)
214 {
215 return false;
216 }
217
218 $sendResult = $this->postSend();
219
220 if ($sendResult)
221 {
222 $this->increaseLimit();
223 }
224
225 return $sendResult;
226 }
227
228 private function prepareCCRecipients($additional_headers)
229 {
230 preg_match(self::HEADER_CC_REGEX, $additional_headers, $matches);
231
232 if ($matches)
233 {
234 $recipients = explode(',', trim($matches['emails']));
235
236 foreach ($recipients as $to)
237 {
238 $to = self::parseAddresses($to) ?? [];
239 if (!$to)
240 {
241 continue;
242 }
243 $this->addCC($to[0]['address'], $to[0]['name']);
244 }
245 }
246 }
247
248 private function prepareBCCRecipients($additional_headers)
249 {
250 preg_match(self::HEADER_BCC_REGEX, $additional_headers, $matches);
251
252 if ($matches)
253 {
254 $recipients = explode(',', trim($matches['emails']));
255
256 foreach ($recipients as $to)
257 {
258 $to = self::parseAddresses($to) ?? [];
259 if (!$to)
260 {
261 continue;
262 }
263 $this->addBCC($to[0]['address'], $to[0]['name']);
264 }
265 }
266 }
267
275 public static function getInstance(Context $context): ?Mailer
276 {
277 $key = hash('sha256', serialize($context));
278 if (!static::$instances[$key])
279 {
280 $mail = new Mailer();
281 if (!$mail->prepareConfiguration($context))
282 {
283 return null;
284 }
285
286 if ($context->getSmtp())
287 {
288 $mail->setFrom($context->getSmtp()->getFrom());
289 }
290
291 switch ($context->getKeepAlive())
292 {
293 default:
295 $mail->SMTPKeepAlive = false;
296 break;
298 $mail->SMTPKeepAlive = true;
299 HttpApplication::getInstance()->addBackgroundJob(
300 function () use ($mail)
301 {
302 $mail->smtpClose();
303 });
304 break;
306 $mail->SMTPKeepAlive = true;
307 break;
308 }
309
310 static::$instances[$key] = $mail;
311 }
312
313 return static::$instances[$key];
314 }
315
324 public static function checkConnect(Context $context, \Bitrix\Main\ErrorCollection $errors): bool
325 {
326 $mail = new Mailer();
327
328 if (\Bitrix\Main\ModuleManager::isModuleInstalled('bitrix24'))
329 {
330 // Private addresses can't be used in the cloud
331 $ip = \Bitrix\Main\Web\IpAddress::createByName($context->getSmtp()->getHost());
332 if ($ip->isPrivate())
333 {
334 $errors->setError(new Error('SMTP server address is invalid'));
335 return false;
336 }
337 }
338
339 if (!$mail->prepareConfiguration($context))
340 {
341 return false;
342 }
343
344 if ($mail->smtpConnect())
345 {
346 $mail->smtpClose();
347 return true;
348 }
349
350 $errors->setError(new Error(Loc::getMessage('main_mail_smtp_connection_failed')));
351 return false;
352 }
353
354 private function checkLimit(): bool
355 {
356 $from = self::parseAddresses($this->From)[0]['address'];
357 $count = count($this->getAllRecipientAddresses());
358
359 $emailCounter = new SenderSendCounter();
360 $emailDailyLimit = Sender::getEmailLimit($from);
361 if($emailDailyLimit
362 && ($emailCounter->get($from) + $count) > $emailDailyLimit)
363 {
364 //daily limit exceeded
365 return false;
366 }
367
368 return true;
369 }
370
371 private function increaseLimit()
372 {
373 $from = self::parseAddresses($this->From)[0]['address'];
374 $emailDailyLimit = Sender::getEmailLimit($from);
375
376 if (!$emailDailyLimit)
377 {
378 return;
379 }
380
381 $emailCounter = new SenderSendCounter();
382 $count = count($this->getAllRecipientAddresses());
383
384 $emailCounter->increment($from, $count);
385 }
386
390 public static function closeConnections(): void
391 {
392 foreach (static::$instances as $instance)
393 {
394 $instance->smtpClose();
395 }
396 }
397
398}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static getEmailLimit($email)
Definition sender.php:311
static getInstance(Context $context)
Definition mailer.php:275
static checkConnect(Context $context, \Bitrix\Main\ErrorCollection $errors)
Definition mailer.php:324
getActualConfiguration(Context $context)
Definition mailer.php:35
prepareConfiguration(Context $context)
Definition mailer.php:63
sendMailBySmtp(string $sourceTo, string $subject, string $message, string $additional_headers, string $additional_parameters)
Definition mailer.php:156
static isModuleInstalled($moduleName)