• Модуль: mail
  • Путь к файлу: ~/bitrix/modules/mail/classes/general/mail.php
  • Класс: CAllMailMessage
  • Вызов: CAllMailMessage::saveMessage
static function saveMessage($mailboxId, &$message, &$header, &$bodyHtml, &$bodyText, &$attachments, $params = array())
{
	global $DB;

	$mailbox_id = $mailboxId;
	$obHeader = &$header;
	$message_body_html = &$bodyHtml;
	$message_body = &$bodyText;
	$arMessageParts = &$attachments;
	$initialHtmlLen = mb_strlen($message_body_html);

	$isStrippedTagsToBody = false;
	$isOriginalEmptyBody = empty(trim(strip_tags($message_body_html)));

	if (self::isLongMessageBody($message_body))
	{
		[$message_body, $message_body_html] = self::prepareLongMessage($message_body, $message_body_html);
	}

	if (
		(mb_strlen($message_body_html) > 0)
		&& empty(trim(strip_tags($message_body_html)))
	)
	{
		$message_body_html = '';
		$isStrippedTagsToBody = true;
	}

	$arFields = array(
		"MAILBOX_ID" => $mailbox_id,
		"HEADER" => $obHeader->strHeader,
		"FIELD_DATE_ORIGINAL" => $obHeader->GetHeader("DATE"),
		"NEW_MESSAGE"	=> "Y",
		"FIELD_FROM" => $obHeader->GetHeader("FROM"),
		"FIELD_REPLY_TO" => $obHeader->GetHeader("REPLY-TO"),
		"FIELD_TO" => $obHeader->GetHeader("TO"),
		"FIELD_CC" => $obHeader->GetHeader("CC"),
		"FIELD_BCC" => ($obHeader->GetHeader('X-Original-Rcpt-to')!=''?$obHeader->GetHeader('X-Original-Rcpt-to').($obHeader->GetHeader("BCC")!=''?', ':''):'').$obHeader->GetHeader("BCC"),
		"MSG_ID" => trim($obHeader->GetHeader("MESSAGE-ID"), " <>"),
		"FIELD_PRIORITY" => intval($obHeader->GetHeader("X-PRIORITY")),
		"MESSAGE_SIZE" => $params['size']?: mb_strlen($message),
		"SUBJECT" => $obHeader->GetHeader("SUBJECT"),
		"BODY" => rtrim($message_body),
		'OPTIONS' => array(
			'attachments' => count($arMessageParts),
			'isStrippedTags' => $isStrippedTagsToBody,
			'isOriginalEmptyBody' => $isOriginalEmptyBody,
		),
	);

	if (
		($arFields['OPTIONS']['attachments'] <= 0)
		&& (empty($message_body) || empty($message_body_html))
	)
	{
		$arFields['OPTIONS']['isEmptyBody'] = 'Y';
	}

	$inReplyTo = trim($obHeader->GetHeader("IN-REPLY-TO"), " <>");

	if($inReplyTo !== '')
	{
		$arFields['IN_REPLY_TO'] = $inReplyTo;
	}

	$datetime = preg_replace('/(?<=[sd])UT$/i', '+0000', $arFields['FIELD_DATE_ORIGINAL']);
	if (!(isset($params['replaces']) && $params['replaces'] > 0) || strtotime($datetime) || $params['timestamp'])
	{
		$timestamp = strtotime($datetime) ?: $params['timestamp'] ?: time();
		$arFields['FIELD_DATE'] = convertTimeStamp($timestamp + CTimeZone::getOffset(), 'FULL');
	}

	if (!empty($message) && BitrixMainConfigOption::get('mail', 'save_src', B_MAIL_SAVE_SRC) == 'Y')
	{
		$arFields['FULL_TEXT'] = $message;
	}

	$forSpamTest = sprintf('%s %s', $arFields['HEADER'], $message_body_html ?: $message_body);

	$arFields["SPAM"] = "?";
	if(COption::GetOptionString("mail", "spam_check", B_MAIL_CHECK_SPAM)=="Y")
	{
		$arSpam = CMailFilter::getSpamRating($forSpamTest);
		$arFields["SPAM_RATING"] = $arSpam["RATING"];
		$arFields["SPAM_WORDS"] = $arSpam["WORDS"];
		$arFields["SPAM_LAST_RESULT"] = "Y";
	}

	// @TODO: MAX_ALLOWED_PACKET
	$arFields['INDEX_VERSION'] = BitrixMailHelperMessageIndexStepper::INDEX_VERSION;
	$arFields['SEARCH_CONTENT'] = BitrixMailHelperMessage::prepareSearchContent($arFields);

	if (isset($params['replaces']) && $params['replaces'] > 0)
	{
		CMailMessage::update($message_id = $params['replaces'], $arFields, $mailbox_id);
	}
	else
	{
		if (isset($params['trackable']) && $params['trackable'])
		{
			$arFields['OPTIONS']['trackable'] = BitrixMainConfigOption::get('main', 'track_outgoing_emails_read', 'Y') == 'Y';
		}

		$message_id = CMailMessage::add($arFields, $mailbox_id);
	}

	if ($message_id > 0)
	{
		$arFields['ID'] = $message_id;
		$arFields['FOR_SPAM_TEST'] = $forSpamTest;

		CMailLog::addMessage(array(
			'MAILBOX_ID'  => $mailbox_id,
			'MESSAGE_ID'  => $message_id,
			'STATUS_GOOD' => 'Y',
			'LOG_TYPE'    => isset($params['replaces']) && $params['replaces'] > 0 ? 'RENEW_MESSAGE' : 'NEW_MESSAGE',
			'MESSAGE'     => sprintf(
				'%s (%s)%s', $arFields['SUBJECT'], $arFields['MESSAGE_SIZE'],
				BitrixMainConfigOption::get('mail', 'spam_check', B_MAIL_CHECK_SPAM) == 'Y'
					? sprintf(' [%.3f]', $arFields['SPAM_RATING']) : ''
			),
		));

		//If the message is new. Not resynchronization
		if (!(isset($params['replaces']) && $params['replaces'] > 0))
		{
			/**
			 * By default, a chain is created for each new message that links the message to itself.
			 * If the parents are not found for the message in the future, then the chain will remain like this.
			 * */
			MessageClosureTable::insertIgnoreFromSql(sprintf('VALUES (%1$u, %1$u)', $message_id));

			/**
			 * We find the parents(in the standard case there should be one) of this message and create a chain.
			 * If the id of the parent (IN_REPLY_TO) matches the id of the message itself(MSG_ID),
			 * then nothing will happen(INSERT IGNORE), since such a chain was created in the step above.
			 * */
			if ($arFields['IN_REPLY_TO'])
			{
				self::makeMessageClosureChain($message_id, $mailbox_id, (string)$arFields['IN_REPLY_TO']);
			}

			$mailbox = BitrixMailMailboxTable::getList(array(
				'select' => array('ID', 'USER_ID', 'OPTIONS'),
				'filter' => array('=ID' => $mailbox_id, '=ACTIVE' => 'Y'),
			))->fetch();

			if ($mailbox['USER_ID'] > 0)
			{
				BitrixMailInternalsMailContactTable::addContactsBatch(array_merge(
					MailContact::getContactsData($arFields['FIELD_TO'], $mailbox['USER_ID'], BitrixMailInternalsMailContactTable::ADDED_TYPE_TO),
					MailContact::getContactsData($arFields['FIELD_FROM'], $mailbox['USER_ID'], BitrixMailInternalsMailContactTable::ADDED_TYPE_FROM),
					MailContact::getContactsData($arFields['FIELD_CC'], $mailbox['USER_ID'], BitrixMailInternalsMailContactTable::ADDED_TYPE_CC),
					MailContact::getContactsData($arFields['FIELD_REPLY_TO'], $mailbox['USER_ID'], BitrixMailInternalsMailContactTable::ADDED_TYPE_REPLY_TO),
					MailContact::getContactsData($arFields['FIELD_BCC'], $mailbox['USER_ID'], BitrixMailInternalsMailContactTable::ADDED_TYPE_BCC)
				));
			}
		}

		$atchCnt = 0;
		if (empty($params['lazy_attachments']) && BitrixMainConfigOption::get('mail', 'save_attachments', B_MAIL_SAVE_ATTACHMENTS) == 'Y')
		{
			foreach ($arMessageParts as $i => $part)
			{
				$attachFields = array(
					'MESSAGE_ID'   => $message_id,
					'FILE_NAME'    => $part['FILENAME'],
					'CONTENT_TYPE' => $part['CONTENT-TYPE'],
					'FILE_DATA'    => $part['BODY'],
					'CONTENT_ID'   => $part['CONTENT-ID'],
				);

				$arMessageParts[$i]['ATTACHMENT-ID'] = CMailMessage::addAttachment($attachFields);
				if (!$arMessageParts[$i]['ATTACHMENT-ID'])
				{
					CMailMessage::delete($message_id);
					return false;
				}

				$atchCnt++;
			}
		}

		$arFields['ATTACHMENTS'] = $atchCnt;

		if ($message_body_html)
		{
			Ini::adjustPcreBacktrackLimit(strlen($message_body_html)*2);

			$msg = array(
				'html'        => $message_body_html,
				'attachments' => array(),
			);
			foreach ($arMessageParts as $part)
			{
				if (!(is_array($part) && $part['ATTACHMENT-ID'] > 0))
				{
					continue;
				}

				$msg['attachments'][] = array(
					'contentId' => $part['CONTENT-ID'],
					'uniqueId'  => sprintf('attachment_%u', $part['ATTACHMENT-ID']),
				);
			}

			$arFields['BODY_BB'] = BitrixMailMessage::parseMessage($msg);

			$arFields['BODY_HTML'] = BitrixMailHelperMessage::sanitizeHtml($message_body_html,false);

			foreach ($arMessageParts as $part)
			{
				if (!(is_array($part) && $part['ATTACHMENT-ID'] > 0))
				{
					continue;
				}

				$arFields['BODY_HTML'] = preg_replace(
					sprintf('/]+)srcs*=s*('|")?s*(http://cid:%s)s*2([^>]*)>/is', preg_quote($part['CONTENT-ID'], '/')),
					sprintf('', $part['ATTACHMENT-ID']),
					$arFields['BODY_HTML']
				);
			}

			$arFields['BODY_HTML'] = BitrixMailHelperMessage::isolateMessageStyles($arFields['BODY_HTML']);

			CMailMessage::update($message_id, array('BODY_HTML' => $arFields['BODY_HTML']), $mailbox_id);
		}
		else
		{
			self::logEmptyHtml($message_id, $initialHtmlLen, $params);
			self::addDefferedDownload($mailboxId, $message_id);
		}

		if (!(isset($params['replaces']) && $params['replaces'] > 0))
		{
			$arFields['IS_OUTCOME'] = !empty($params['outcome']);
			$arFields['IS_DRAFT'] = !empty($params['draft']);
			$arFields['IS_TRASH'] = !empty($params['trash']);
			$arFields['IS_SPAM'] = !empty($params['spam']);
			$arFields['IS_SEEN'] = !empty($params['seen']);
			$arFields['MSG_HASH'] = $params['hash'];

			if (!empty($params['excerpt']) && is_array($params['excerpt']))
			{
				$arFields = $arFields + $params['excerpt'];
			}

			$messageBindings = array();

			$eventKey = BitrixMainEventManager::getInstance()->addEventHandler(
				'mail',
				'onBeforeUserFieldSave',
				function (BitrixMainEvent $event) use (&$messageBindings)
				{
					$params = $event->getParameters();
					$messageBindings[] = $params['entity_type'];
				}
			);

			$arFieldsForFilter = $arFields;

			foreach (['BODY','BODY_BB','BODY_HTML','SUBJECT'] as $key)
			{
				if(!empty($arFieldsForFilter[$key]))
				{
					$arFieldsForFilter[$key] = Emoji::decode($arFieldsForFilter[$key]);
				}
			}

			CMailFilter::filter($arFieldsForFilter, 'R');

			BitrixMainEventManager::getInstance()->removeEventHandler('mail', 'onBeforeUserFieldSave', $eventKey);

			$event = new BitrixMainEvent('mail', 'onMailMessageNew', [
				'message'     => $arFields,
				'attachments' => $arMessageParts,
				'userId'      => isset($mailbox['USER_ID']) ? $mailbox['USER_ID'] : null,
			]);
			$event->send();

			addEventToStatFile(
				'mail',
				sprintf(
					'add_%s_%s',
					(empty($arFields['IN_REPLY_TO']) ? 'message' : 'reply'),
					(empty($params['outcome']) ? 'incoming' : 'outgoing')
				),
				join(',', array_unique(array_filter($messageBindings))),
				$arFields['MSG_ID']
			);
		}
	}

	return $message_id;
}