Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/components/bitrix/mail.client/ |
Upload File : |
<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die(); use Bitrix\Bitrix24\MailCounter; use Bitrix\Mail; use Bitrix\Mail\Helper\Message; use Bitrix\Mail\Helper\MessageFolder; use Bitrix\Mail\ImapCommands\MailsFlagsManager; use Bitrix\Mail\ImapCommands\MailsFoldersManager; use Bitrix\Mail\Integration\Calendar\ICal\ICalMailManager; use Bitrix\Mail\Integration\Intranet\Secretary; use Bitrix\Mail\Internals\MessageAccessTable; use Bitrix\Mail\MailMessageTable; use Bitrix\Main; use Bitrix\Main\Context; use Bitrix\Main\Error; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Mail\Helper; use Bitrix\Main\Mail\Sender; use Bitrix\Main\Mail\SenderSendCounter; Loader::includeModule('mail'); Loc::loadLanguageFile(__FILE__); Loc::loadMessages(__DIR__ . '/../mail.client/class.php'); class CMailClientAjaxController extends \Bitrix\Main\Engine\Controller { /** @var bool */ private $isCrmEnable = false; private const CRM_TYPES = [ 'contact', 'company', 'lead', ]; /** * Initializes controller. * @return void */ protected function init() { parent::init(); $this->isCrmEnable = \Bitrix\Mail\Integration\Crm\Permissions::getInstance()->hasAccessToCrm(); } /** * Common operations before process action. * * @param \Bitrix\Main\Engine\Action $action Action. * * @return bool If method will return false, then action will not execute. * @throws Main\LoaderException */ protected function processBeforeAction(\Bitrix\Main\Engine\Action $action) { if (parent::processBeforeAction($action)) { if ($action->getName() === 'sendMessage') { $data = $this->request->getPost('data'); if (empty($data)) { $this->addError(new Error('Source data are not found')); } } } return (count($this->getErrors()) === 0); } /** * Move messages to folder. * @param string[] $ids * @param string $folder */ public function moveToFolderAction($ids, $folder) { $resultIds = $this->getIds($ids); if ($resultIds->isSuccess()) { $data = $resultIds->getData(); $mailMarkerManager = new MailsFoldersManager($data['mailboxId'], $data['messagesIds'], $this->getCurrentUser()->getId()); $mailboxId = $data['mailboxId']; $idsUnseenCount = Mail\MailMessageUidTable::getCount([ '!@IS_SEEN' => ['Y', 'S'], '@ID' => $data['messagesIds'], '=MAILBOX_ID' => $mailboxId, ]); $dirWithMessagesId = false; $dirForMoveMessagesId = []; if($idsUnseenCount) { $dirWithMessagesId = MessageFolder::getDirIdForMessages($mailboxId,$data['messagesIds']); $dirForMoveMessagesId = Mail\Internals\MailboxDirectoryTable::getList([ 'select' => [ 'ID', ], 'filter' => [ '=PATH' => $folder, '=MAILBOX_ID' => $mailboxId, ], 'limit' => 1, ])->fetchAll(); } $result = $mailMarkerManager->moveMails($folder); if (!$result->isSuccess()) { $errors = $result->getErrors(); $this->addError($errors[0]); } else { if($dirWithMessagesId && isset($dirForMoveMessagesId[0]['ID'])) { $dirForMoveMessagesId = $dirForMoveMessagesId[0]['ID']; MessageFolder::decreaseDirCounter($mailboxId, $dirWithMessagesId, $idsUnseenCount); $mailboxHelper = Helper\Mailbox::createInstance($mailboxId); $dirForMoveMessages = $mailboxHelper->getDirsHelper()->getDirByPath($folder); MessageFolder::increaseDirCounter($mailboxId, $dirForMoveMessages, $dirForMoveMessagesId, $idsUnseenCount); \Bitrix\Mail\Helper::updateMailboxUnseenCounter($mailboxId); $mailboxHelper->updateGlobalCounterForCurrentUser(); } } } } protected function markMessages($ids, $seen = true) { $method = ($seen ? 'markMailsSeen' : 'markMailsUnseen'); if (!empty($ids['for_all'])) { [$mailboxId, $dir] = explode('-', $ids['for_all']); $ids = array(); $res = Mail\MailMessageUidTable::getList(array( 'select' => array('ID'), 'filter' => array( '=MAILBOX_ID' => $mailboxId, '=DIR_MD5' => md5($dir), '>MESSAGE_ID' => 0, '@IS_SEEN' => $seen ? array('N', 'U') : array('Y', 'S'), '==DELETE_TIME' => 0, ), )); while ($item = $res->fetch()) { $ids[] = "{$item['ID']}-{$mailboxId}"; } } $result = $this->getIds($ids,true); if ($result->isSuccess()) { $data = $result->getData(); $mailMarkerManager = new MailsFlagsManager($data['mailboxId'], $data['messagesIds']); $result = $mailMarkerManager->$method(); if (!$result->isSuccess()) { $errors = $result->getErrors(); $this->addError($errors[0]); } } } /** * Mark messages as unseen. * @param string[] $ids */ public function markAsUnseenAction($ids) { $this->markMessages($ids, false); } /** * Mark messages as seen. * @param string[] $ids */ public function markAsSeenAction($ids) { $this->markMessages($ids, true); } /** * Restore messages from spam. * @param $ids */ public function restoreFromSpamAction($ids) { $result = $this->getIds($ids); if ($result->isSuccess()) { $data = $result->getData(); $mailboxId = $data['mailboxId']; $messagesIds = $data['messagesIds']; $idsUnseenCount = Mail\MailMessageUidTable::getCount([ '!@IS_SEEN' => ['Y', 'S'], '@ID' => $messagesIds, '=MAILBOX_ID' => $mailboxId, ]); $mailMarkerManager = new MailsFoldersManager($data['mailboxId'], $data['messagesIds'], $this->getCurrentUser()->getId()); $result = $mailMarkerManager->restoreMailsFromSpam(); if (!$result->isSuccess()) { $errors = $result->getErrors(); $this->addError($errors[0]); } else { if($idsUnseenCount) { $dirForMoveMessagesId = Mail\Internals\MailboxDirectoryTable::getList([ 'select' => [ 'ID', ], 'filter' => [ '=PATH' => 'INBOX', '=MAILBOX_ID' => $mailboxId, ], 'limit' => 1, ])->fetchAll(); if(isset($dirForMoveMessagesId[0]['ID'])) { $dirForMoveMessagesId = $dirForMoveMessagesId[0]['ID']; $mailboxHelper = Helper\Mailbox::createInstance($mailboxId); $dirForMoveMessages = $mailboxHelper->getDirsHelper()->getDirByPath('INBOX'); MessageFolder::increaseDirCounter($mailboxId, $dirForMoveMessages, $dirForMoveMessagesId, $idsUnseenCount); Helper::updateMailboxUnseenCounter($mailboxId); $mailboxHelper->updateGlobalCounterForCurrentUser(); } } } } } /** * Marks messages as spam. * @param string[] $ids * * @throws Main\ArgumentException * @throws Main\ObjectPropertyException * @throws Main\SystemException */ public function markAsSpamAction($ids) { $result = $this->getIds($ids); if ($result->isSuccess()) { $data = $result->getData(); $mailboxId = $data['mailboxId']; $messagesIds = $data['messagesIds']; $dirWithMessagesId = MessageFolder::getDirIdForMessages($mailboxId,$messagesIds); $idsUnseenCount = Mail\MailMessageUidTable::getCount([ '!@IS_SEEN' => ['Y', 'S'], '@ID' => $messagesIds, '=MAILBOX_ID' => $mailboxId, ]); $mailMarkerManager = new MailsFoldersManager($mailboxId, $messagesIds, $this->getCurrentUser()->getId()); $result = $mailMarkerManager->sendMailsToSpam(); if (!$result->isSuccess()) { $errors = $result->getErrors(); $this->addError($errors[0]); } else { MessageFolder::decreaseDirCounter($mailboxId, $dirWithMessagesId, $idsUnseenCount); $mailboxHelper = Helper\Mailbox::createInstance($mailboxId); Helper::updateMailboxUnseenCounter($mailboxId); $mailboxHelper->updateGlobalCounterForCurrentUser(); } } } /** * Deletes messages. * @param string[] $ids * @param boolean $deleteImmediately */ public function deleteAction($ids, $deleteImmediately = false) { $result = $this->getIds($ids); if ($result->isSuccess()) { $data = $result->getData(); $mailboxId = $data['mailboxId']; $messagesIds = $data['messagesIds']; $mailMarkerManager = new MailsFoldersManager($mailboxId, $messagesIds); $dirWithMessagesId = MessageFolder::getDirIdForMessages($mailboxId,$messagesIds); $idsUnseenCount = Mail\MailMessageUidTable::getCount([ '!@IS_SEEN' => ['Y', 'S'], '@ID' => $messagesIds, '=MAILBOX_ID' => $mailboxId, ]); $result = $mailMarkerManager->deleteMails($deleteImmediately); if (!$result->isSuccess()) { $errors = $result->getErrors(); $this->addError($errors[0]); } else if($idsUnseenCount) { MessageFolder::decreaseDirCounter($mailboxId, $dirWithMessagesId, $idsUnseenCount); $mailboxHelper = Helper\Mailbox::createInstance($mailboxId); Helper::updateMailboxUnseenCounter($mailboxId); $mailboxHelper->updateGlobalCounterForCurrentUser(); } } } /** * @param $ids * * @return \Bitrix\Main\Result */ private function getIds($ids, $ignoreOld = false) { $result = new \Bitrix\Main\Result(); if (empty($ids)) { return $result->addError(new \Bitrix\Main\Error('validation')); } $mailboxIds = $messIds = []; foreach ($ids as $id) { [$messId, $mailboxId] = explode('-', $id, 2); $mailboxIds[$mailboxId] = $mailboxId; $messIds[$messId] = $messId; } if (count($mailboxIds) > 1) { return $result->addError(new \Bitrix\Main\Error('validation')); } if($ignoreOld) { $oldIds = Mail\MailMessageUidTable::getList(array( 'select' => array('ID'), 'filter' => array( '@ID' => $messIds, '=MAILBOX_ID' => current($mailboxIds), '=IS_OLD' => 'Y', ), ))->fetchAll(); foreach ($oldIds as $item) { if(is_set($messIds[$item['ID']])) { unset($messIds[$item['ID']]); } } } if (!count($mailboxIds)) { return $result->addError(new \Bitrix\Main\Error('validation')); } if (!count($messIds)) { return $result->addError(new \Bitrix\Main\Error('validation')); } $result->setData([ 'mailboxId' => array_pop($mailboxIds), 'messagesIds' => array_keys($messIds), ]); return $result; } /** * Generates message Id. * @param string $hostname * * @return string */ private function generateMessageId($hostname) { // @TODO: more entropy return sprintf( '<bx.mail.%x.%x@%s>', time(), rand(0, 0xffffff), $hostname ); } /** * Generates message Id for CRM email. * @param string $hostname * @param string $urn * * @return string */ private function generateCrmMessageId($hostname, $urn) { return sprintf('<crm.activity.%s@%s>', $urn, $hostname); } /** * Gets host name. * * @return string */ private function getHostname() { static $hostname; if (empty($hostname)) { $hostname = \COption::getOptionString('main', 'server_name', '') ?: 'localhost'; if (defined('BX24_HOST_NAME') && BX24_HOST_NAME != '') { $hostname = BX24_HOST_NAME; } elseif (defined('SITE_SERVER_NAME') && SITE_SERVER_NAME != '') { $hostname = SITE_SERVER_NAME; } } return $hostname; } /** * @param $id * @param $dir (dir path) * @param $onlySyncCurrent * * @return array * @throws Exception */ public function syncMailboxAction($id, $dir, $onlySyncCurrent = false) { $sessionId = md5(uniqid('')); $response = array( 'complete' => false, 'status' => 0, 'sessid' => $sessionId, 'timestamp' => microtime(true), 'final' => true, 'is_fatal_error' => false, ); if(!Loader::includeModule('mail')) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_MODULE_NOT_INSTALLED')); //Stop attempts to resynchronize the mailbox $response['is_fatal_error'] = true; $response['complete'] = true; return $response; } if (!Mail\Helper\LicenseManager::isSyncAvailable()) { $response['complete'] = true; $response['is_fatal_error'] = true; $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_SYNC_NOT_AVAILABLE')); return $response; } $mailbox = \Bitrix\Mail\MailboxTable::getUserMailbox($id); if ($mailbox) { session_write_close(); $mailboxHelper = Helper\Mailbox::createInstance($id); $mailboxHelper->setSyncParams(array( 'full' => true, 'currentDir' => $dir, 'sessid' => $sessionId, )); $mailboxSyncManager = new Mail\Helper\Mailbox\MailboxSyncManager($mailbox['USER_ID']); $mailboxSyncManager->setSyncStartedData($id); $result = $mailboxHelper->syncDir($dir); $response['timestamp'] = microtime(true); if ($result === false) { $mailboxSyncManager->setSyncStatus($id, false, time()); $this->errorCollection->add($mailboxHelper->getWarnings()->toArray()); $response['complete'] = true; $response['is_fatal_error'] = true; } else { /* If the directory is not locked for synchronization, then we will resynchronize the old messages (delete the missing messages, synchronize the readability statuses). */ if ($result !== null) { $response['new'] = $result; $lastSyncResult = $mailboxHelper->getLastSyncResult(); $response['updated'] = -$lastSyncResult['updatedMessages']; $response['deleted'] = -$lastSyncResult['deletedMessages']; $mailboxHelper->resyncDir($dir); $lastSyncResult = $mailboxHelper->getLastSyncResult(); $response['updated'] += $lastSyncResult['updatedMessages']; $response['deleted'] += $lastSyncResult['deletedMessages']; $response['timestamp'] = microtime(true); } $onlySyncCurrent = filter_var($onlySyncCurrent, FILTER_VALIDATE_BOOLEAN); if (!$onlySyncCurrent && count($mailboxHelper->getDirsHelper()->getSyncDirs()) > 1) { //If resynchronization of the entire mailbox is started if($mailboxHelper->sync(false)) { $response['complete'] = true; } } else { $mailboxSyncManager->setSyncStatus($id, true, time()); $mailboxHelper->notifyNewMessages(); $response['complete'] = true; } } } else { $response['complete'] = true; $response['is_fatal_error'] = true; $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_THE_MAILBOX_HAS_BEEN_DELETED')); } if($mailbox && $response['new'] > 0 || $response['deleted'] > 0 || $response['updated'] > 0) { $mailboxHelper->syncCounters(); $mailboxHelper->sendCountersEvent(); } return $response; } /** * Sends email. * * @param array $data * * @return void * * @throws Exception * @throws Main\NotImplementedException * @throws Main\SystemException */ public function sendMessageAction($data) { $rawData = (array) \Bitrix\Main\Application::getInstance()->getContext()->getRequest()->getPostList()->getRaw('data'); $decodedData = $rawData; $hostname = $this->getHostname(); $fromEmail = $decodedData['from']; $fromAddress = new \Bitrix\Main\Mail\Address($fromEmail); $responsibleId = $this->getCurrentUser()->getId(); if ($fromAddress->validate()) { $fromEmail = $fromAddress->getEmail(); \CBitrixComponent::includeComponentClass('bitrix:main.mail.confirm'); if (!in_array($fromEmail, array_column(\MainMailConfirmComponent::prepareMailboxes(), 'email'))) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_MESSAGE_BAD_SENDER')); return; } if ($fromAddress->getName()) { $fromEncoded = sprintf( '%s <%s>', sprintf('=?%s?B?%s?=', SITE_CHARSET, base64_encode($fromAddress->getName())), $fromEmail ); } } else { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage( empty($fromEmail) ? 'MAIL_MESSAGE_EMPTY_SENDER' : 'MAIL_MESSAGE_BAD_SENDER' )); return; } $to = array(); $cc = array(); $bcc = array(); $toEncoded = array(); $ccEncoded = array(); $bccEncoded = array(); if ($this->isCrmEnable) { $crmCommunication = array(); } foreach (array('to', 'cc', 'bcc') as $field) { if (!empty($rawData[$field]) && is_array($rawData[$field])) { $addressList = array(); foreach ($rawData[$field] as $item) { try { $item = \Bitrix\Main\Web\Json::decode($item); $address = new Bitrix\Main\Mail\Address(); $address->setEmail($item['email']); $address->setName(htmlspecialcharsBack($item['name'])); if ($address->validate()) { $fieldEncoded = $field.'Encoded'; if ($address->getName()) { ${$field}[] = $address->get(); ${$fieldEncoded}[] = $address->getEncoded(); } else { ${$field}[] = $address->getEmail(); ${$fieldEncoded}[] = $address->getEmail(); } $addressList[] = $address; if ($this->isCrmEnable) { if (isset($item['entityType'])) { if (in_array($item['entityType'], self::CRM_TYPES, true)) { $crmCommunication[] = $item; } } } } } catch (\Exception $e) { } } if (count($addressList) > 0) { $this->appendMailContacts($addressList, $field); } } } $to = array_unique($to); $cc = array_unique($cc); $bcc = array_unique($bcc); $toEncoded = array_unique($toEncoded); $ccEncoded = array_unique($ccEncoded); $bccEncoded = array_unique($bccEncoded); $emailsLimitToSendMessage = Helper\LicenseManager::getEmailsLimitToSendMessage(); if($emailsLimitToSendMessage !== -1 && (count($to) > $emailsLimitToSendMessage || count($cc) > $emailsLimitToSendMessage || count($bcc) > $emailsLimitToSendMessage)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_MESSAGE_NEW_TARIFF_RESTRICTION', ['#COUNT#'=> $emailsLimitToSendMessage])); return; } if (count($to) + count($cc) + count($bcc) > 10) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_MESSAGE_TO_MANY_RECIPIENTS')); return; } if (empty($to)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_MESSAGE_EMPTY_RCPT')); return; } $totalRecipientsCount = count($to) + count($cc) + count($bcc); if ($this->isSenderLimitReached((string)$fromEmail, $totalRecipientsCount)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_DAILY_SENDER_LIMIT_REACHED')); return; } if ($this->isDailyPortalLimitReached($totalRecipientsCount)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_DAILY_PORTAL_LIMIT_REACHED')); return; } if ($this->isMonthPortalLimitReached($totalRecipientsCount)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_MONTH_PORTAL_LIMIT_REACHED')); return; } $messageBody = (string) $decodedData['message']; $messageBodyHtml = ''; if (!empty($messageBody)) { $messageBody = preg_replace('/<!--.*?-->/is', '', $messageBody); $messageBody = preg_replace('/<script[^>]*>.*?<\/script>/is', '', $messageBody); $messageBody = preg_replace('/<title[^>]*>.*?<\/title>/is', '', $messageBody); $sanitizer = new \CBXSanitizer(); $sanitizer->setLevel(\CBXSanitizer::SECURE_LEVEL_LOW); $sanitizer->applyDoubleEncode(false); $sanitizer->addTags(Helper\Message::getWhitelistTagAttributes()); $messageBody = $sanitizer->sanitizeHtml($messageBody); $messageBodyHtml = $messageBody; $messageBody = preg_replace('/https?:\/\/bxacid:(n?\d+)/i', 'bxacid:\1', $messageBody); } $outgoingBody = $messageBody; $totalSize = 0; $attachments = array(); $attachmentIds = array(); if (!empty($data['__diskfiles']) && is_array($data['__diskfiles']) && Loader::includeModule('disk')) { foreach ($data['__diskfiles'] as $item) { if (!preg_match('/n\d+/i', $item)) { continue; } $id = ltrim($item, 'n'); if (!($diskFile = \Bitrix\Disk\File::loadById($id))) { continue; } if (!($file = \CFile::makeFileArray($diskFile->getFileId()))) { continue; } $totalSize += $diskFile->getSize(); $attachmentIds[] = $id; $contentId = sprintf( 'bxacid.%s@%s.mail', hash('crc32b', $file['external_id'].$file['size'].$file['name']), hash('crc32b', $hostname) ); $attachments[] = array( 'ID' => $contentId, 'NAME' => $diskFile->getName(), 'PATH' => $file['tmp_name'], 'CONTENT_TYPE' => $file['type'], ); $outgoingBody = preg_replace( sprintf('/(https?:\/\/)?bxacid:n?%u/i', $id), sprintf('cid:%s', $contentId), $outgoingBody ); } } $maxSize = Helper\Message::getMaxAttachedFilesSize(); if ($maxSize > 0 && $maxSize <= ceil($totalSize / 3) * 4) // base64 coef. { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage( 'MAIL_MESSAGE_MAX_SIZE_EXCEED', ['#SIZE#' => \CFile::formatSize(Helper\Message::getMaxAttachedFilesSizeAfterEncoding(),1)] )); return; } $mailboxHelper = Mail\Helper\Mailbox::findBy($data['MAILBOX_ID'], $fromEmail); $mailboxOwnerId = null; if (!empty($mailboxHelper)) { $mailboxOwnerId = $mailboxHelper->getMailboxOwnerId(); if (!$mailboxHelper->isAuthenticated()) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_IMAP_ERR_AUTH')); return; } } $outgoingParams = [ 'CHARSET' => SITE_CHARSET, 'CONTENT_TYPE' => 'html', 'ATTACHMENT' => $attachments, 'TO' => implode(', ', $toEncoded), 'SUBJECT' => $data['subject'], 'BODY' => $outgoingBody, 'HEADER' => [ 'From' => $fromEncoded ?: $fromEmail, 'Reply-To' => $fromEncoded ?: $fromEmail, 'Cc' => implode(', ', $ccEncoded), 'Bcc' => implode(', ', $bccEncoded), ], ]; if(isset($data['IN_REPLY_TO'])) { $outgoingParams['HEADER']['In-Reply-To']=sprintf('<%s>', $data['IN_REPLY_TO']); } $messageBindings = array(); // crm activity if ($this->isCrmEnable && count($crmCommunication) > 0) { $messageFields = array_merge( $outgoingParams, array( 'BODY' => $messageBodyHtml, 'FROM' => $fromEmail, 'TO' => $to, 'CC' => $cc, 'BCC' => $bcc, 'IMPORTANT' => !empty($data['important']), 'STORAGE_TYPE_ID' => \Bitrix\Crm\Integration\StorageType::Disk, 'STORAGE_ELEMENT_IDS' => $attachmentIds, ) ); $activityFields = [ 'RESPONSIBLE_ID' => $responsibleId, 'EDITOR_ID' => $responsibleId, 'AUTHOR_ID' => $mailboxOwnerId, 'COMMUNICATIONS' => $crmCommunication, ]; if (\CCrmEMail::createOutgoingMessageActivity($messageFields, $activityFields) !== true) { if (!empty($activityFields['ERROR_TEXT'])) { $this->errorCollection[] = new \Bitrix\Main\Error($activityFields['ERROR_TEXT']); } elseif (!empty($activityFields['ERROR_CODE'])) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_' . $activityFields['ERROR_CODE'])); } else { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_ACTIVITY_CREATE_ERROR')); } return; } $messageBindings[] = Mail\Internals\MessageAccessTable::ENTITY_TYPE_CRM_ACTIVITY; //$activityId = $activityFields['ID']; //$urn = $messageFields['URN']; $messageId = $messageFields['MSG_ID']; } else { $messageId = $this->generateMessageId($hostname); } $outgoingParams['HEADER']['Message-Id'] = $messageId; if (empty($mailboxHelper)) { $context = new Main\Mail\Context(); $context->setCategory(Main\Mail\Context::CAT_EXTERNAL); $context->setPriority( isset($addressList) && count($addressList) > 2 ? Main\Mail\Context::PRIORITY_LOW : Main\Mail\Context::PRIORITY_NORMAL ); $result = Main\Mail\Mail::send(array_merge( $outgoingParams, array( 'CONTEXT' => $context, ) )); } else { $eventKey = Main\EventManager::getInstance()->addEventHandler( 'mail', 'onBeforeUserFieldSave', function (\Bitrix\Main\Event $event) use (&$messageBindings) { $params = $event->getParameters(); $messageBindings[] = $params['entity_type']; } ); $mailboxHelper->mail(array_merge( $outgoingParams, array( 'HEADER' => array_merge( $outgoingParams['HEADER'], array( 'To' => $outgoingParams['TO'], 'Subject' => $outgoingParams['SUBJECT'], ) ), ) )); Main\EventManager::getInstance()->removeEventHandler('mail', 'onBeforeUserFieldSave', $eventKey); } addEventToStatFile( 'mail', (empty($data['IN_REPLY_TO']) ? 'send_message' : 'send_reply'), join(',', array_unique(array_filter($messageBindings))), trim(trim($messageId), '<>') ); return; } private function isSenderLimitReached(string $fromEmail, int $recipientsCount): bool { $emailDailyLimit = Sender::getEmailLimit($fromEmail); if ($emailDailyLimit <= 0) { return false; } $emailCounter = new SenderSendCounter(); $limit = $emailCounter->get($fromEmail); return ($limit + $recipientsCount) > $emailDailyLimit; } private function isDailyPortalLimitReached(int $recipientsCount): bool { if (!isModuleInstalled('bitrix24') || !Loader::includeModule('bitrix24')) { return false; } $counter = new MailCounter(); $limit = $counter->getDailyLimit(); return $limit > 0 && MailCounter::checkLimit($limit, $counter->get() + $recipientsCount); } private function isMonthPortalLimitReached(int $recipientsCount): bool { if (!isModuleInstalled('bitrix24') || !Loader::includeModule('bitrix24')) { return false; } $counter = new MailCounter(); $limit = $counter->getLimit(); return $limit > 0 && MailCounter::checkLimit($limit, $counter->getMonthly() + $recipientsCount); } /** * Creates crm activity. * * @param string $messageId * * @return array|void * @throws Main\ArgumentException * @throws Main\LoaderException * @throws Main\ObjectPropertyException * @throws Main\SystemException */ public function createCrmActivityAction($messageId, $iteration = 1) { if (!Loader::includeModule('crm')) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_AJAX_ERROR')); return; } $message = Mail\MailMessageTable::getList(array( 'runtime' => array( new Main\Entity\ReferenceField( 'MESSAGE_UID', 'Bitrix\Mail\MailMessageUidTable', array( '=this.MAILBOX_ID' => 'ref.MAILBOX_ID', '=this.ID' => 'ref.MESSAGE_ID', ), array( 'join_type' => 'INNER', ) ), ), 'select' => array( '*', 'MAILBOX_EMAIL' => 'MAILBOX.EMAIL', 'MAILBOX_NAME' => 'MAILBOX.NAME', 'MAILBOX_LOGIN' => 'MAILBOX.LOGIN', 'IS_SEEN' => 'MESSAGE_UID.IS_SEEN', 'MSG_HASH' => 'MESSAGE_UID.HEADER_MD5', 'DIR_MD5' => 'MESSAGE_UID.DIR_MD5', 'MSG_UID' => 'MESSAGE_UID.MSG_UID', ), 'filter' => array( '=ID' => $messageId, ), 'order' => array( 'FIELD_DATE' => 'DESC', 'MESSAGE_UID.ID' => 'DESC', 'MESSAGE_UID.MSG_UID' => 'ASC', ), 'limit' => 1, ))->fetch(); if (empty($message)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_ELEMENT_NOT_FOUND')); return; } if (!Mail\Helper\Message::hasAccess($message)) { $this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_ELEMENT_DENIED')); return; } if ($iteration <= 1 && Mail\Helper\Message::ensureAttachments($message) > 0) { return $this->createCrmActivityAction($messageId, $iteration + 1); } Mail\Helper\Message::prepare($message); $message['IS_OUTCOME'] = $message['__is_outcome']; //$message['IS_TRASH'] = !empty($params['trash']); //$message['IS_SPAM'] = !empty($params['spam']); $message['IS_SEEN'] = in_array($message['IS_SEEN'], array('Y', 'S')); $message['__forced'] = true; $this->sanitizeHtmlForOldCrmModule($message); if (!\CCrmEMail::imapEmailMessageAdd($message, null, $error)) { $this->addError(new Error(Loc::getMessage('MAIL_MESSAGE_LIST_NOTIFY_ADD_TO_CRM_ERROR'))); if ($error) { $this->addError($error instanceof Error ? $error : new Error($error)); } } } /** * Sanitization of mail html body if crm module not able to sanitize on view * (crm module version < 23.1300.0) * * @param array $message Message fields */ private function sanitizeHtmlForOldCrmModule(array &$message): void { $crmFilterSettings = \CCrmEMail::onGetFilterListImap(); if (empty($crmFilterSettings['SANITIZE_ON_VIEW']) && !empty($message[MailMessageTable::FIELD_SANITIZE_ON_VIEW]) && !empty($message['BODY_HTML'])) { $message['BODY_HTML'] = \Bitrix\Mail\Helper\Message::sanitizeHtml($message['BODY_HTML'], true); } } /** * Removes crm activity. * @param string $messageId * * @return array|void * @throws Main\ArgumentException * @throws Main\DB\SqlQueryException * @throws Main\LoaderException * @throws Main\ObjectPropertyException * @throws Main\SystemException */ public function removeCrmActivityAction($messageId) { global $USER; if (!Loader::includeModule('crm')) { $this->errorCollection[] = new Main\Error(Loc::getMessage('MAIL_CLIENT_AJAX_ERROR')); return; } $message = Mail\MailMessageTable::getList(array( 'select' => array( '*', 'MAILBOX_EMAIL' => 'MAILBOX.EMAIL', 'MAILBOX_NAME' => 'MAILBOX.NAME', 'MAILBOX_LOGIN' => 'MAILBOX.LOGIN', ), 'filter' => array( '=ID' => $messageId, ), ))->fetch(); if (empty($message)) { $this->errorCollection[] = new Main\Error(Loc::getMessage('MAIL_CLIENT_ELEMENT_NOT_FOUND')); return; } $crmEntityIds = $this->getBindCrmEntityIds($message['MAILBOX_ID'], $messageId); if (empty($crmEntityIds)) { $this->errorCollection[] = new Main\Error(Loc::getMessage('MAIL_CLIENT_ELEMENT_NOT_FOUND')); return; } $mailbox = Mail\MailboxTable::getUserMailbox($message['MAILBOX_ID']); if (empty($mailbox)) { $this->errorCollection[] = new Main\Error(Loc::getMessage('MAIL_CLIENT_ELEMENT_DENIED')); return; } $result = array(); Mail\Helper\Message::prepare($message); if (empty($message['__is_outcome'])) { $exclusionAccess = new \Bitrix\Crm\Exclusion\Access($USER->getId()); if ($exclusionAccess->canWrite()) { foreach (array_merge($message['__from'], $message['__reply_to']) as $item) { if (!empty($item['email'])) { \Bitrix\Crm\Exclusion\Store::add(\Bitrix\Crm\Communication\Type::EMAIL, $item['email']); } } } } foreach ($crmEntityIds as $item) { \CCrmActivity::delete($item); } return $result; } /** * Append contact reference. * * @param \Bitrix\Main\Mail\Address[] $addressList Email address list. * @param string $fromField Email field TO|CC|BCC. * * @return void * @throws Main\ArgumentException * @throws Main\Db\SqlQueryException * @throws Main\ObjectPropertyException * @throws Main\SystemException */ private function appendMailContacts($addressList, $fromField = '') { $fromField = mb_strtoupper($fromField); if ( !in_array( $fromField, array( \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_TO, \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_CC, \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_BCC, ) ) ) { $fromField = \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_TO; } $allEmails = array(); $contactsData = array(); /** * @var \Bitrix\Main\Mail\Address $address */ foreach ($addressList as $address) { $allEmails[] = mb_strtolower($address->getEmail()); $contactsData[] = array( 'USER_ID' => $this->getCurrentUser()->getId(), 'NAME' => $address->getName(), 'ICON' => \Bitrix\Mail\Helper\MailContact::getIconData($address->getEmail(), $address->getName()), 'EMAIL' => $address->getEmail(), 'ADDED_FROM' => $fromField, ); } \Bitrix\Mail\Internals\MailContactTable::addContactsBatch($contactsData); $mailContacts = \Bitrix\Mail\Internals\MailContactTable::query() ->addSelect('ID') ->where('USER_ID', $this->getCurrentUser()->getId()) ->whereIn('EMAIL', $allEmails) ->exec(); $lastRcpt = array(); while ($contact = $mailContacts->fetch()) { $lastRcpt[] = 'MC'. $contact['ID']; } if (count($lastRcpt) > 0) { \Bitrix\Main\FinderDestTable::merge(array( 'USER_ID' => $this->getCurrentUser()->getId(), 'CONTEXT' => 'MAIL_LAST_RCPT', 'CODE' => $lastRcpt, )); } } /** * Get mail crm activity entity ids * * @param int $mailboxId Mailbox ID * @param int $messageId Message ID * * @return array|int[] */ private function getBindCrmEntityIds(int $mailboxId, int $messageId): array { $binds = MessageAccessTable::query() ->where('MAILBOX_ID', $mailboxId) ->where('MESSAGE_ID', $messageId) ->where('ENTITY_TYPE', 'CRM_ACTIVITY') ->setDistinct() ->addSelect('ENTITY_ID') ->fetchAll(); return array_column($binds, 'ENTITY_ID'); } public function icalAction() { $request = Context::getCurrent()->getRequest(); $messageId = (int)$request->getPost('messageId'); $action = (string)$request->getPost('action'); if (!$messageId || !$action) { $this->addError(new Error(Loc::getMessage('MAIL_CLIENT_FORM_ERROR'))); return []; } $message = MailMessageTable::getList([ 'runtime' => [ new Main\Entity\ReferenceField( 'MAILBOX', 'Bitrix\Mail\MailboxTable', [ '=this.MAILBOX_ID' => 'ref.ID', ], [ 'join_type' => 'INNER', ] ), ], 'select' => [ 'ID', 'FIELD_FROM', 'FIELD_TO', 'OPTIONS', 'USER_ID' => 'MAILBOX.USER_ID', ], 'filter' => [ '=ID' => $messageId, ], ])->fetch(); if (empty($message['OPTIONS']['iCal'])) { return []; } $icalComponent = ICalMailManager::parseRequest($message['OPTIONS']['iCal']); if ($icalComponent instanceof \Bitrix\Calendar\ICal\Parser\Calendar && $icalComponent->getMethod() === \Bitrix\Calendar\ICal\Parser\Dictionary::METHOD['request'] && $icalComponent->hasOneEvent() ) { $handler = \Bitrix\Calendar\ICal\MailInvitation\IncomingInvitationRequestHandler::createInstance(); $result = $handler->setDecision($action) ->setIcalComponent($icalComponent) ->setUserId((int)$message['USER_ID']) ->setEmailFrom($message['FIELD_FROM']) ->setEmailTo($message['FIELD_TO']) ->handle() ; $eventId = $handler->getEventId(); if ($result && ($eventId > 0)) { Secretary::provideAccessToMessage( $message['ID'], Message::ENTITY_TYPE_CALENDAR_EVENT, $eventId, $this->getCurrentUser()->getId() ); } return [ 'eventId' => $eventId, ]; } return []; } }