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;
}