73 if(array_key_exists(
'LINK_PROTOCOL', $mailParams) && $mailParams[
'LINK_PROTOCOL'] <>
'')
75 $this->trackLinkProtocol = $mailParams[
'LINK_PROTOCOL'];
78 if(array_key_exists(
'TRACK_READ', $mailParams) && !empty($mailParams[
'TRACK_READ']))
81 $mailParams[
'TRACK_READ'][
'MODULE_ID'],
82 $mailParams[
'TRACK_READ'][
'FIELDS'],
83 $mailParams[
'TRACK_READ'][
'URL_PAGE'] ??
null
86 if(array_key_exists(
'TRACK_CLICK', $mailParams) && !empty($mailParams[
'TRACK_CLICK']))
89 $mailParams[
'TRACK_CLICK'][
'MODULE_ID'],
90 $mailParams[
'TRACK_CLICK'][
'FIELDS'],
91 $mailParams[
'TRACK_CLICK'][
'URL_PAGE'] ??
null
93 if(!empty($mailParams[
'TRACK_CLICK'][
'URL_PARAMS']))
95 $this->trackClickUrlParams = $mailParams[
'TRACK_CLICK'][
'URL_PARAMS'];
99 if(array_key_exists(
'LINK_DOMAIN', $mailParams) && $mailParams[
'LINK_DOMAIN'] <>
'')
101 $this->settingServerName = $mailParams[
'LINK_DOMAIN'];
104 $this->charset = $mailParams[
'CHARSET'];
105 $this->contentType = $mailParams[
'CONTENT_TYPE'];
106 $this->messageId = $mailParams[
'MESSAGE_ID'] ??
null;
109 $this->attachment = ($mailParams[
'ATTACHMENT'] ?? array());
110 if (isset($mailParams[
'USE_BLACKLIST']))
112 $this->useBlacklist = (bool) $mailParams[
'USE_BLACKLIST'];
117 if (!$this->trackReadAvailable)
119 $this->trackReadLink =
null;
122 if (!$this->trackClickAvailable)
124 $this->trackClickLink =
null;
127 if (isset($mailParams[
'GENERATE_TEXT_VERSION']))
129 $this->generateTextVersion = (bool) $mailParams[
'GENERATE_TEXT_VERSION'];
133 $this->
setTo($mailParams[
'TO']);
135 $this->
setBody($mailParams[
'BODY']);
139 if(array_key_exists(
'CONTEXT', $mailParams) && is_object($mailParams[
'CONTEXT']))
141 $this->context = $mailParams[
'CONTEXT'];
162 public static function send($mailParams)
166 $event = new \Bitrix\Main\Event(
"main",
"OnBeforeMailSend", array($mailParams));
168 foreach ($event->getResults() as $eventResult)
170 if($eventResult->getType() == \
Bitrix\Main\EventResult::ERROR)
173 $mailParams = array_merge($mailParams, $eventResult->getParameters());
176 if(defined(
"ONLY_EMAIL") && $mailParams[
'TO'] != ONLY_EMAIL)
182 $mail = static::createInstance($mailParams);
183 if ($mail->canSend())
185 $mailResult = bxmail(
190 $mail->getAdditionalParameters(),
229 if(defined(
"BX_MS_SMTP") && BX_MS_SMTP===
true)
231 $this->settingServerMsSmtp =
true;
234 if(
Config\Option::get(
"main",
"fill_to_mail",
"N")==
"Y")
236 $this->settingMailFillToEmail =
true;
238 if(
Config\Option::get(
"main",
"convert_mail_header",
"Y")==
"Y")
240 $this->settingMailConvertMailHeader =
true;
242 if(
Config\Option::get(
"main",
"send_mid",
"N")==
"Y")
244 $this->settingMailAddMessageId =
true;
246 if(
Config\Option::get(
"main",
"CONVERT_UNIX_NEWLINE_2_WINDOWS",
"N")==
"Y")
248 $this->settingConvertNewLineUnixToWindows =
true;
250 if(
Config\Option::get(
"main",
"attach_images",
"N")==
"Y")
252 $this->settingAttachImages =
true;
254 if(
Config\Option::get(
"main",
"mail_encode_base64",
"N") ==
"Y")
256 $this->settingMailEncodeBase64 =
true;
258 else if (
Config\Option::get(
'main',
'mail_encode_quoted_printable',
'N') ==
'Y')
260 $this->settingMailEncodeQuotedPrintable =
true;
263 if(!isset($this->settingServerName) || $this->settingServerName ==
'')
265 $this->settingServerName = Config\Option::get(
"main",
"server_name",
"");
268 if (!$this->trackLinkProtocol)
270 $this->trackLinkProtocol = Config\Option::get(
"main",
"mail_link_protocol") ?:
"http";
273 $this->generateTextVersion = Config\Option::get(
"main",
"mail_gen_text_version",
"Y") ===
'Y';
275 $this->settingMaxFileSize = intval(
Config\Option::get(
"main",
"max_file_size"));
277 $this->settingMailAdditionalParameters = Config\Option::get(
"main",
"mail_additional_parameters",
"");
281 $this->trackReadAvailable = Config\Option::get(
'main',
'track_outgoing_emails_read',
'Y') ==
'Y';
282 $this->trackClickAvailable = Config\Option::get(
'main',
'track_outgoing_emails_click',
'Y') ==
'Y';
309 $plainPart =
new Part();
310 $plainPart->addHeader(
'Content-Type',
'text/plain; charset=' .
$charset);
312 if($this->contentType ==
"html")
316 $bodyPart = $this->trackRead($bodyPart);
319 $htmlPart =
new Part();
320 $htmlPart->addHeader(
'Content-Type',
'text/html; charset=' .
$charset);
321 $htmlPart->setBody($bodyPart);
327 $plainPart->setBody($bodyPart);
330 $cteName =
'Content-Transfer-Encoding';
331 $cteValue = $this->contentTransferEncoding;
333 if ($this->settingMailEncodeBase64)
335 $cteValue =
'base64';
337 else if ($this->settingMailEncodeQuotedPrintable)
339 $cteValue =
'quoted-printable';
342 $this->multipart->addHeader($cteName, $cteValue);
343 $plainPart->addHeader($cteName, $cteValue);
346 $htmlPart->addHeader($cteName, $cteValue);
355 $this->multipartRelated->addPart($htmlPart);
356 $htmlPart = $this->multipartRelated;
359 if ($this->generateTextVersion)
362 $alternative->addPart($plainPart);
363 $alternative->addPart($htmlPart);
364 $this->multipart->addPart($alternative);
368 $this->multipart->addPart($htmlPart);
373 $this->multipart->addPart($plainPart);
378 $body = $this->multipart->toStringBody();
380 if($this->settingConvertNewLineUnixToWindows)
434 $files = $this->attachment;
435 if(is_array($this->filesReplacedFromBody))
437 $files = array_merge($files, array_values($this->filesReplacedFromBody));
445 $isLimitExceeded = $this->isFileLimitExceeded(
446 !empty($attachment[
"SIZE"]) ? $attachment[
"SIZE"] : strlen($attachment[
"CONTENT"] ??
''),
450 if (!$isLimitExceeded)
454 $fileContent = $attachment[
"CONTENT"] ?? File::getFileContents($attachment[
"PATH"]);
456 catch (\Exception $exception)
466 $isLimitExceeded = $this->isFileLimitExceeded(
467 strlen($fileContent),
470 if ($isLimitExceeded)
472 $attachment[
"NAME"] = $attachment[
"NAME"] .
'.txt';
473 $attachment[
'CONTENT_TYPE'] =
'text/plain';
474 $fileContent = str_replace(
475 [
'%name%',
'%limit%'],
478 round($this->settingMaxFileSize / 1024 / 1024, 1),
480 'This is not the original file. The size of the original file `%name%` exceeded the limit of %limit% MB.'
484 if(isset($attachment[
'METHOD']))
486 $name = $this->
encodeSubject($attachment[
"NAME"], $attachment[
'CHARSET']);
488 ->addHeader(
'Content-Type', $attachment[
'CONTENT_TYPE'] .
489 "; name=\"$name\"; method=".$attachment[
'METHOD'].
"; charset=".$attachment[
'CHARSET'])
490 ->addHeader(
'Content-Disposition',
"attachment; filename=\"$name\"")
491 ->addHeader(
'Content-Transfer-Encoding',
'base64')
492 ->addHeader(
'Content-ID',
"<{$attachment['ID']}>")
493 ->setBody($fileContent);
497 $name = $this->
encodeSubject($attachment[
"NAME"], $this->charset);
499 ->addHeader(
'Content-Type', $attachment[
'CONTENT_TYPE'] .
"; name=\"$name\"")
500 ->addHeader(
'Content-Disposition',
"attachment; filename=\"$name\"")
501 ->addHeader(
'Content-Transfer-Encoding',
'base64')
502 ->addHeader(
'Content-ID',
"<{$attachment['ID']}>")
503 ->setBody($fileContent);
506 if ($this->multipartRelated && $this->isAttachmentImage($attachment,
true))
508 $this->multipartRelated->addPart($part);
512 $this->multipart->addPart($part);
725 $resultTo = static::toPunycode($this->to);
727 if($this->settingMailConvertMailHeader)
729 $resultTo = static::encodeHeaderFrom($resultTo, $this->charset);
732 if($this->settingServerMsSmtp)
734 $resultTo = preg_replace(
"/(.*)\\<(.*)\\>/i",
'$2', $resultTo);
899 if(array_key_exists($src, $this->filesReplacedFromBody))
901 $uid = $this->filesReplacedFromBody[$src][
"ID"];
902 return $matches[1].$matches[2].
"cid:".$uid.$matches[4].$matches[5];
905 $uri =
new Uri($src);
907 $io = \CBXVirtualIo::GetInstance();
908 $filePath = $io->GetPhysicalName($filePath);
909 if(!File::isFileExists($filePath))
914 foreach($this->attachment as $attachIndex => $attach)
916 if($filePath == $attach[
'PATH'])
918 $this->attachment[$attachIndex][
'RELATED'] =
true;
919 return $matches[1].$matches[2].
"cid:".$attach[
'ID'].$matches[4].$matches[5];
923 if ($this->settingMaxFileSize > 0)
925 $fileIoObject =
new File($filePath);
926 if ($fileIoObject->getSize() > $this->settingMaxFileSize)
933 $imageInfo = (new \Bitrix\Main\File\Image($filePath))->getInfo();
939 if (function_exists(
"image_type_to_mime_type"))
941 $contentType = image_type_to_mime_type($imageInfo->getFormat());
948 $uid = uniqid(md5($src));
950 $this->filesReplacedFromBody[$src] = array(
955 "NAME" => bx_basename($src),
959 return $matches[1].$matches[2].
"cid:".$uid.$matches[4].$matches[5];
974 $srcTrimmed = trim($src);
975 if(mb_substr($srcTrimmed, 0, 2) ==
"//")
977 $src = $this->trackLinkProtocol .
":" . $srcTrimmed;
979 else if(mb_substr($srcTrimmed, 0, 1) ==
"/")
981 $srcModified =
false;
982 if(!empty($this->attachment))
984 $io = \CBXVirtualIo::GetInstance();
986 foreach($this->attachment as $attachIndex => $attach)
988 if($filePath == $attach[
'PATH'])
990 $this->attachment[$attachIndex][
'RELATED'] =
true;
991 $src =
"cid:".$attach[
'ID'];
1000 $src = $this->trackLinkProtocol .
"://".$this->settingServerName . $srcTrimmed;
1005 if (mb_stripos($matches[0],
'<img') === 0 && !preg_match(
"/<img[^>]*?\\s+alt\\s*=[^>]+>/is", $matches[0]))
1010 return $matches[1] . $matches[2] . $src . $matches[4] . $add . $matches[5];
1022 $replaceImageFunction =
'getReplacedImageSrc';
1023 if($this->settingAttachImages)
1024 $replaceImageFunction =
'getReplacedImageCid';
1026 $this->filesReplacedFromBody = array();
1027 $textReplaced = preg_replace_callback(
1028 "/(<img\\s[^>]*?(?<=\\s)src\\s*=\\s*)([\"']?)(.*?)(\\2)(\\s.+?>|\\s*>)/is",
1029 array($this, $replaceImageFunction),
1032 if($textReplaced !==
null) $text = $textReplaced;
1034 $textReplaced = preg_replace_callback(
1035 "/(background\\s*:\\s*url\\s*\\(|background-image\\s*:\\s*url\\s*\\()([\"']?)(.*?)(\\2)(\\s*\\)(.*?);)/is",
1036 array($this, $replaceImageFunction),
1039 if($textReplaced !==
null) $text = $textReplaced;
1041 $textReplaced = preg_replace_callback(
1042 "/(<td\\s[^>]*?(?<=\\s)background\\s*=\\s*)([\"']?)(.*?)(\\2)(\\s.+?>|\\s*>)/is",
1043 array($this, $replaceImageFunction),
1046 if($textReplaced !==
null) $text = $textReplaced;
1048 $textReplaced = preg_replace_callback(
1049 "/(<table\\s[^>]*?(?<=\\s)background\\s*=\\s*)([\"']?)(.*?)(\\2)(\\s.+?>|\\s*>)/is",
1050 array($this, $replaceImageFunction),
1053 if($textReplaced !==
null) $text = $textReplaced;
1111 $href = $matches[3];
1117 if(mb_substr($href, 0, 2) ==
'//')
1119 $href = $this->trackLinkProtocol .
':' . $href;
1122 if(mb_substr($href, 0, 1) ==
'/')
1124 $href = $this->trackLinkProtocol .
'://' . $this->settingServerName . $href;
1127 if($this->trackClickLink)
1129 if($this->trackClickUrlParams)
1132 foreach($this->trackClickUrlParams as $k => $v)
1133 $hrefAddParam .=
'&'.htmlspecialcharsbx($k).
'='.htmlspecialcharsbx($v);
1135 $parsedHref = explode(
"#", $href);
1136 $parsedHref[0] .= (strpos($parsedHref[0],
'?') ===
false?
'?' :
'&').mb_substr($hrefAddParam, 1);
1137 $href = implode(
"#", $parsedHref);
1140 $href = $this->trackClickLink .
'&url=' . urlencode($href) .
'&sign=' . urlencode(
Tracking::getSign($href));
1141 if (!preg_match(
'/^http:\/\/|https:\/\//', $this->trackClickLink))
1143 $href = $this->trackLinkProtocol .
'://' . $this->settingServerName . $href;
1147 return $matches[1].$matches[2].$href.$matches[4].$matches[5];
1199 if (!$this->useBlacklist || !Internal\BlacklistTable::hasBlacklistedEmails())
1205 $allEmails = [mb_strtolower($this->to)];
1208 foreach ($headers as $name => $value)
1211 if (!in_array(mb_strtolower($name), static::$emailHeaders))
1217 $emails = explode(
',', $value);
1218 foreach ($emails as $email)
1220 $email = trim($email);
1226 $address =
new Address($email);
1227 $email = $address->getEmail();
1230 $list[$name][] = $address;
1231 $allEmails[] = $address->getEmail();
1237 $allEmails = array_diff($allEmails, $this->blacklistCheckedEmails);
1238 if (!empty($allEmails))
1240 $blacklisted = Internal\BlacklistTable::getList([
1241 'select' => [
'CODE'],
1242 'filter' => [
'=CODE' => $allEmails]
1244 $blacklisted = array_column($blacklisted,
'CODE');
1246 $this->blacklistedEmails = array_unique(array_merge($this->blacklistedEmails, $blacklisted));
1247 $this->blacklistCheckedEmails = array_merge($this->blacklistCheckedEmails, $allEmails);
1250 if (empty($this->blacklistedEmails))
1256 $blacklisted = $this->blacklistedEmails;
1257 foreach ($headers as $name => $value)
1260 if (!in_array(mb_strtolower($name), static::$emailHeaders))
1265 $emails = array_filter(
1267 function (
Address $address) use ($blacklisted)
1270 return $email && !in_array($email, $blacklisted);
1274 $emails = array_map(
1282 $emails = implode(
', ', $emails);
1286 unset($headers[$name]);
1290 $headers[$name] = $emails;