%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/mail/install/components/bitrix/mail.client/ |
| Current File : /home/bitrix/www/bitrix/modules/mail/install/components/bitrix/mail.client/ajax.php |
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
use Bitrix\Mail\ImapCommands\MailsFlagsManager;
use Bitrix\Mail\ImapCommands\MailsFoldersManager;
use Bitrix\Main;
use Bitrix\Main\Localization\Loc;
use Bitrix\Mail;
use Bitrix\Main\Error;
Main\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;
/**
* Initializes controller.
* @return void
*/
protected function init()
{
parent::init();
$this->isCrmEnable = Main\Loader::includeModule('crm') && \CCrmPerms::isAccessEnabled();
}
/**
* 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)
{
$result = $this->getIds($ids);
if ($result->isSuccess())
{
$data = $result->getData();
$mailMarkerManager = new MailsFoldersManager($data['mailboxId'], $data['messagesIds'], $this->getCurrentUser()->getId());
$result = $mailMarkerManager->moveMails($folder);
if (!$result->isSuccess())
{
$errors = $result->getErrors();
$this->addError($errors[0]);
}
}
}
/**
* Mark messages as unseen.
* @param string[] $ids
*/
public function markAsUnseenAction($ids)
{
$result = $this->getIds($ids);
if ($result->isSuccess())
{
$data = $result->getData();
$mailMarkerManager = new MailsFlagsManager($data['mailboxId'], $data['messagesIds']);
$result = $mailMarkerManager->markMailsUnseen();
if (!$result->isSuccess())
{
$errors = $result->getErrors();
$this->addError($errors[0]);
}
}
}
/**
* Mark messages as seen.
* @param string[] $ids
*/
public function markAsSeenAction($ids)
{
$result = $this->getIds($ids);
if ($result->isSuccess())
{
$data = $result->getData();
$mailMarkerManager = new MailsFlagsManager($data['mailboxId'], $data['messagesIds']);
$result = $mailMarkerManager->markMailsSeen();
if (!$result->isSuccess())
{
$errors = $result->getErrors();
$this->addError($errors[0]);
}
}
}
/**
* Restore messages from spam.
* @param $ids
*/
public function restoreFromSpamAction($ids)
{
$result = $this->getIds($ids);
if ($result->isSuccess())
{
$data = $result->getData();
$mailMarkerManager = new MailsFoldersManager($data['mailboxId'], $data['messagesIds']);
$result = $mailMarkerManager->restoreMailsFromSpam();
if (!$result->isSuccess())
{
$errors = $result->getErrors();
$this->addError($errors[0]);
}
}
}
/**
* 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();
$mailMarkerManager = new MailsFoldersManager($data['mailboxId'], $data['messagesIds'], $this->getCurrentUser()->getId());
$result = $mailMarkerManager->sendMailsToSpam();
if (!$result->isSuccess())
{
$errors = $result->getErrors();
$this->addError($errors[0]);
}
}
}
/**
* Deletes messages.
* @param string[] $ids
*/
public function deleteAction($ids)
{
$result = $this->getIds($ids);
if ($result->isSuccess())
{
$data = $result->getData();
$mailMarkerManager = new MailsFoldersManager($data['mailboxId'], $data['messagesIds']);
$result = $mailMarkerManager->deleteMails();
if (!$result->isSuccess())
{
$errors = $result->getErrors();
$this->addError($errors[0]);
}
}
}
/**
* @param $ids
*
* @return \Bitrix\Main\Result
*/
private function getIds($ids)
{
$result = new \Bitrix\Main\Result();
if (empty($ids))
{
return $result->addError(new \Bitrix\Main\Error('validation'));
}
$mailboxIds = $messIds = [];
foreach ($ids as $index => $id)
{
list($messId, $mailboxId) = $this->parseMessageId($id);
if (!$this->validateId($messId) || !is_numeric($mailboxId))
{
continue;
}
$mailboxIds[$mailboxId] = $mailboxId;
$messIds[$messId] = $messId;
}
if (count($mailboxIds) > 1)
{
return $result->addError(new \Bitrix\Main\Error('validation'));
}
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;
}
/**
* @param $id
*
* @return array
*/
private function parseMessageId($id)
{
return explode('-', $id);
}
/**
* 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);
}
/**
* Validate message Id.
* @param string $id
*
* @return bool
*/
private function validateId($id)
{
return strlen($id) == 32;
}
/**
* 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
*
* @return array
* @throws Exception
*/
public function syncMailboxAction($id)
{
$response = array(
'new' => 0,
'complete' => false,
'status' => -1,
);
if ($mailbox = \Bitrix\Mail\MailboxTable::getUserMailbox($id))
{
session_write_close();
$mailboxHelper = \Bitrix\Mail\Helper\Mailbox::createInstance($id);
$result = $mailboxHelper->sync();
if ($result === false)
{
$this->errorCollection->add($mailboxHelper->getErrors()->toArray());
}
else
{
$response['new'] = $result;
$response['complete'] = $mailboxHelper->getMailbox()['SYNC_LOCK'] < 0;
$response['status'] = $mailboxHelper->getSyncStatus();
}
}
else
{
$this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_FORM_ERROR'));
}
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;
\CUtil::decodeUriComponent($decodedData);
$hostname = $this->getHostname();
$fromEmail = $decodedData['from'];
$fromAddress = new \Bitrix\Main\Mail\Address($fromEmail);
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($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)
{
// crm only
if (strpos($item['id'], 'CRM') === 0)
{
$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);
if (empty($to))
{
$this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_MESSAGE_EMPTY_RCPT'));
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->applyHtmlSpecChars(false);
$sanitizer->addTags(array('style' => array()));
$messageBody = $sanitizer->sanitizeHtml($messageBody);
$messageBodyHtml = $messageBody;
$messageBody = preg_replace('/https?:\/\/bxacid:(n?\d+)/i', 'bxacid:\1', $messageBody);
}
$outgoingBody = $messageBody;
$attachments = array();
$attachmentIds = array();
if (!empty($data['__diskfiles']) && is_array($data['__diskfiles']))
{
foreach ($data['__diskfiles'] as $item)
{
$id = ltrim($item, 'n');
$diskFile = \Bitrix\Disk\File::loadById($id);
$file = \CFile::makeFileArray($diskFile->getFileId());
$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
);
}
}
if ($data['MAILBOX_ID'] > 0)
{
if ($mailbox = Mail\MailboxTable::getUserMailbox($data['MAILBOX_ID']))
{
$mailboxHelper = Mail\Helper\Mailbox::createInstance($mailbox['ID'], false);
}
}
if (!($mailboxHelper instanceof \Bitrix\Mail\Helper\Mailbox\Imap))
{
foreach (Mail\MailboxTable::getUserMailboxes() as $mailbox)
{
if ($fromEmail == $mailbox['EMAIL'])
{
$mailboxHelper = Mail\Helper\Mailbox::createInstance($mailbox['ID'], false);
}
}
}
$outgoingParams = array(
'CHARSET' => SITE_CHARSET,
'CONTENT_TYPE' => 'html',
'ATTACHMENT' => $attachments,
'TO' => implode(', ', $toEncoded),
'SUBJECT' => $data['subject'],
'BODY' => $outgoingBody,
'HEADER' => array(
'From' => $fromEncoded ?: $fromEmail,
'Reply-To' => $fromEncoded ?: $fromEmail,
//'To' => join(', ', $to),
'Cc' => implode(', ', $ccEncoded),
'Bcc' => implode(', ', $bccEncoded),
//'Subject' => $data['subject'],
//'Message-Id' => $messageId,
'In-Reply-To' => sprintf('<%s>', $data['IN_REPLY_TO']),
),
);
// 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 = array(
'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($activityFields['ERROR_CODE']));
}
else
{
$this->errorCollection[] = new \Bitrix\Main\Error(Loc::getMessage('MAIL_CLIENT_ACTIVITY_CREATE_ERROR'));
}
return;
}
//$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(Main\Mail\Context::PRIORITY_NORMAL);
$result = Main\Mail\Mail::send(array_merge(
$outgoingParams,
array(
'CONTEXT' => $context,
)
));
}
else
{
$result = $mailboxHelper->mail(array_merge(
$outgoingParams,
array(
'HEADER' => array_merge(
$outgoingParams['HEADER'],
array(
'To' => $outgoingParams['TO'],
'Subject' => $outgoingParams['SUBJECT'],
)
),
)
));
}
return;
}
/**
* 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)
{
if (!\Bitrix\Main\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',
),
'filter' => array(
'=ID' => $messageId,
),
'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;
}
$result = array();
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler(
'mail',
'onBeforeUserFieldSave',
function (\Bitrix\Main\Event $event) use (&$message, &$result)
{
$params = $event->getParameters();
if ($params['mailbox_id'] == $message['MAILBOX_ID'] && $params['message_id'] == $message['ID'])
{
if ($params['entity_type'] == 'CRM_ACTIVITY')
{
$result[] = $params['entity_id'];
}
}
}
);
Mail\Helper\Message::prepare($message);
$message['IS_OUTCOME'] = $message['__is_outcome'];
$message['__forced'] = true;
\CCrmEMail::imapEmailMessageAdd($message);
return $result;
}
/**
* 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 (!Main\Loader::includeModule('crm'))
{
$this->errorCollection[] = new Main\Error(Loc::getMessage('MAIL_CLIENT_AJAX_ERROR'));
return;
}
$message = Mail\MailMessageTable::getList(array(
'runtime' => array(
new Main\Entity\ReferenceField(
'MESSAGE_ACCESS',
Mail\Internals\MessageAccessTable::class,
array(
'=this.MAILBOX_ID' => 'ref.MAILBOX_ID',
'=this.ID' => 'ref.MESSAGE_ID',
)
),
),
'select' => array(
'*',
'MAILBOX_EMAIL' => 'MAILBOX.EMAIL',
'MAILBOX_NAME' => 'MAILBOX.NAME',
'MAILBOX_LOGIN' => 'MAILBOX.LOGIN',
new Main\Entity\ExpressionField(
'BIND',
'GROUP_CONCAT(%s)',
'MESSAGE_ACCESS.ENTITY_ID'
),
),
'filter' => array(
'=ID' => $messageId,
'=MESSAGE_ACCESS.ENTITY_TYPE' => 'CRM_ACTIVITY',
),
))->fetch();
if (empty($message))
{
$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)
{
\Bitrix\Crm\Exclusion\Store::add(\Bitrix\Crm\Communication\Type::EMAIL, $item['email']);
}
}
}
foreach (explode(',', $message['BIND']) 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 = 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[] = 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,
));
}
}
}