PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/main/lib/mail/sender.php

https://gitlab.com/alexprowars/bitrix
PHP | 594 lines | 484 code | 76 blank | 34 comment | 79 complexity | a3fb0b9a1311cd007cd4e53ddef7ba8e MD5 | raw file
  1. <?php
  2. namespace Bitrix\Main\Mail;
  3. use Bitrix\Main;
  4. use Bitrix\Main\Engine\CurrentUser;
  5. use Bitrix\Main\Mail\Internal\SenderTable;
  6. use Bitrix\Main\Event;
  7. class Sender
  8. {
  9. public const MAIN_SENDER_SMTP_LIMIT_DECREASE = 'MainSenderSmtpLimitDecrease';
  10. public static function add(array $fields)
  11. {
  12. if (empty($fields['OPTIONS']) || !is_array($fields['OPTIONS']))
  13. {
  14. $fields['OPTIONS'] = array();
  15. }
  16. self::checkEmail($fields, $error, $errors);
  17. if ($error || $errors)
  18. {
  19. return array('error' => $error, 'errors' => $errors);
  20. }
  21. if (empty($fields['IS_CONFIRMED']))
  22. {
  23. $fields['OPTIONS']['confirm_code'] = \Bitrix\Main\Security\Random::getStringByCharsets(5, '0123456789abcdefghjklmnpqrstuvwxyz');
  24. $fields['OPTIONS']['confirm_time'] = time();
  25. }
  26. $senderId = 0;
  27. $result = Internal\SenderTable::add($fields);
  28. if ($result->isSuccess())
  29. {
  30. $senderId = $result->getId();
  31. }
  32. if (empty($fields['IS_CONFIRMED']))
  33. {
  34. $mailEventFields = array(
  35. 'DEFAULT_EMAIL_FROM' => $fields['EMAIL'],
  36. 'EMAIL_TO' => $fields['EMAIL'],
  37. 'MESSAGE_SUBJECT' => getMessage('MAIN_MAIL_CONFIRM_MESSAGE_SUBJECT'),
  38. 'CONFIRM_CODE' => mb_strtoupper($fields['OPTIONS']['confirm_code']),
  39. );
  40. if (!empty($smtpConfig))
  41. {
  42. \Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
  43. 'main',
  44. 'OnBeforeEventSend',
  45. function (&$eventFields, &$message, $context) use (&$smtpConfig)
  46. {
  47. $context->setSmtp($smtpConfig);
  48. }
  49. );
  50. }
  51. \CEvent::sendImmediate('MAIN_MAIL_CONFIRM_CODE', SITE_ID, $mailEventFields);
  52. }
  53. else
  54. {
  55. if (isset($fields['OPTIONS']['__replaces']) && $fields['OPTIONS']['__replaces'] > 0)
  56. {
  57. Internal\SenderTable::delete(
  58. (int) $fields['OPTIONS']['__replaces']
  59. );
  60. }
  61. }
  62. return ['senderId' => $senderId, 'confirmed' => !empty($fields['IS_CONFIRMED'])];
  63. }
  64. /**
  65. * Check smtp connection
  66. * @param $fields
  67. * @param null $error
  68. * @param Main\ErrorCollection|null $errors
  69. */
  70. public static function checkEmail(&$fields, &$error = null, Main\ErrorCollection &$errors = null)
  71. {
  72. if (empty($fields['IS_CONFIRMED']) && !empty($fields['OPTIONS']['smtp']))
  73. {
  74. $smtpConfig = $fields['OPTIONS']['smtp'];
  75. $smtpConfig = new Smtp\Config(array(
  76. 'from' => $fields['EMAIL'],
  77. 'host' => $smtpConfig['server'],
  78. 'port' => $smtpConfig['port'],
  79. 'protocol' => $smtpConfig['protocol'],
  80. 'login' => $smtpConfig['login'],
  81. 'password' => $smtpConfig['password'],
  82. ));
  83. if ($smtpConfig->canCheck())
  84. {
  85. if ($smtpConfig->check($error, $errors))
  86. {
  87. $fields['IS_CONFIRMED'] = true;
  88. }
  89. }
  90. }
  91. }
  92. public static function confirm($ids)
  93. {
  94. if (!empty($ids))
  95. {
  96. $res = Internal\SenderTable::getList(array(
  97. 'filter' => array(
  98. '@ID' => (array) $ids,
  99. ),
  100. ));
  101. while ($item = $res->fetch())
  102. {
  103. Internal\SenderTable::update(
  104. (int) $item['ID'],
  105. array(
  106. 'IS_CONFIRMED' => true,
  107. )
  108. );
  109. if (isset($item['OPTIONS']['__replaces']) && $item['OPTIONS']['__replaces'] > 0)
  110. {
  111. Internal\SenderTable::delete(
  112. (int) $item['OPTIONS']['__replaces']
  113. );
  114. }
  115. }
  116. }
  117. }
  118. public static function delete($ids)
  119. {
  120. if(!is_array($ids))
  121. {
  122. $ids = [$ids];
  123. }
  124. if(empty($ids))
  125. {
  126. return;
  127. }
  128. $smtpConfigs = [];
  129. $senders = SenderTable::getList([
  130. 'order' => [
  131. 'ID' => 'desc',
  132. ],
  133. 'filter' => [
  134. '=USER_ID' => CurrentUser::get()->getId(),
  135. '@ID' => $ids,
  136. 'IS_CONFIRMED' => true]
  137. ]
  138. )->fetchAll();
  139. foreach($senders as $sender)
  140. {
  141. if(!empty($sender['OPTIONS']['smtp']['server']) && empty($sender['OPTIONS']['smtp']['encrypted']) && !isset($smtpConfigs[$sender['EMAIL']]))
  142. {
  143. $smtpConfigs[$sender['EMAIL']] = $sender['OPTIONS']['smtp'];
  144. }
  145. }
  146. if(!empty($smtpConfigs))
  147. {
  148. $senders = SenderTable::getList([
  149. 'order' => [
  150. 'ID' => 'desc',
  151. ],
  152. 'filter' => [
  153. '@EMAIL' => array_keys($smtpConfigs),
  154. '!ID' => $ids
  155. ]
  156. ])->fetchAll();
  157. foreach($senders as $sender)
  158. {
  159. if(isset($smtpConfigs[$sender['EMAIL']]))
  160. {
  161. $options = $sender['OPTIONS'];
  162. $options['smtp'] = $smtpConfigs[$sender['EMAIL']];
  163. $result = SenderTable::update($sender['ID'], [
  164. 'OPTIONS' => $options,
  165. ]);
  166. if($result->isSuccess())
  167. {
  168. unset($smtpConfigs[$sender['EMAIL']]);
  169. static::clearCustomSmtpCache($sender['EMAIL']);
  170. }
  171. if(empty($smtpConfigs))
  172. {
  173. break;
  174. }
  175. }
  176. }
  177. }
  178. foreach ((array) $ids as $id)
  179. {
  180. Internal\SenderTable::delete(
  181. (int) $id
  182. );
  183. }
  184. }
  185. public static function clearCustomSmtpCache($email)
  186. {
  187. $cache = new \CPHPCache();
  188. $cache->clean($email, '/main/mail/smtp');
  189. }
  190. public static function getCustomSmtp($email)
  191. {
  192. static $smtp = array();
  193. if (!isset($smtp[$email]))
  194. {
  195. $config = false;
  196. $cache = new \CPHPCache();
  197. if ($cache->initCache(30*24*3600, $email, '/main/mail/smtp'))
  198. {
  199. $config = $cache->getVars();
  200. }
  201. else
  202. {
  203. $res = Internal\SenderTable::getList(array(
  204. 'filter' => array(
  205. 'IS_CONFIRMED' => true,
  206. '=EMAIL' => $email,
  207. ),
  208. 'order' => array(
  209. 'ID' => 'DESC',
  210. ),
  211. ));
  212. while ($item = $res->fetch())
  213. {
  214. if (!empty($item['OPTIONS']['smtp']['server']) && empty($item['OPTIONS']['smtp']['encrypted']))
  215. {
  216. $config = $item['OPTIONS']['smtp'];
  217. break;
  218. }
  219. }
  220. $cache->startDataCache();
  221. $cache->endDataCache($config);
  222. }
  223. if ($config)
  224. {
  225. $config = new Smtp\Config(array(
  226. 'from' => $email,
  227. 'host' => $config['server'],
  228. 'port' => $config['port'],
  229. 'protocol' => $config['protocol'],
  230. 'login' => $config['login'],
  231. 'password' => $config['password'],
  232. ));
  233. }
  234. $smtp[$email] = $config;
  235. }
  236. return $smtp[$email];
  237. }
  238. /**
  239. * get sending limit by email, returns null if no limit.
  240. * @param $email
  241. * @return int|null
  242. * @throws Main\ArgumentException
  243. * @throws Main\ObjectPropertyException
  244. * @throws Main\SystemException
  245. */
  246. public static function getEmailLimit($email): ?int
  247. {
  248. $address = new \Bitrix\Main\Mail\Address($email);
  249. if (!$address->validate())
  250. {
  251. return null;
  252. }
  253. $email = $address->getEmail();
  254. static $mailLimit = array();
  255. if (!isset($mailLimit[$email]))
  256. {
  257. $cache = new \CPHPCache();
  258. if ($cache->initCache(3600, $email, '/main/mail/limit'))
  259. {
  260. $mailLimit[$email] = $cache->getVars();
  261. }
  262. else
  263. {
  264. $res = Internal\SenderTable::getList(array(
  265. 'filter' => array(
  266. 'IS_CONFIRMED' => true,
  267. '=EMAIL' => $email,
  268. ),
  269. 'order' => array(
  270. 'ID' => 'DESC',
  271. ),
  272. ));
  273. $limit = null;
  274. while ($item = $res->fetch())
  275. {
  276. if ($item['OPTIONS']['smtp']['limit'] !== null)
  277. {
  278. $limit = (int)$item['OPTIONS']['smtp']['limit'];
  279. break;
  280. }
  281. }
  282. $mailLimit[$email] = $limit;
  283. $cache->startDataCache();
  284. $cache->endDataCache($mailLimit[$email]);
  285. }
  286. }
  287. return $mailLimit[$email] < 0 ? 0 : $mailLimit[$email];
  288. }
  289. /**
  290. * Set sender limit by email. Finding all senders with same email and set up limit from option
  291. * Returns true if change some email limit.
  292. * Returns false if has no changes.
  293. * @param string $email
  294. * @param int $limit
  295. * @return bool
  296. * @throws Main\ArgumentException
  297. * @throws Main\ObjectPropertyException
  298. * @throws Main\SystemException
  299. */
  300. public static function setEmailLimit(string $email, int $limit, bool $quite = true): bool
  301. {
  302. $address = new \Bitrix\Main\Mail\Address($email);
  303. if (!$address->validate())
  304. {
  305. return false;
  306. }
  307. $email = $address->getEmail();
  308. $cache = new \CPHPCache();
  309. $cache->clean($email, '/main/mail/limit');
  310. $res = Internal\SenderTable::getList(array(
  311. 'filter' => array(
  312. 'IS_CONFIRMED' => true,
  313. '=EMAIL' => $email,
  314. ),
  315. 'order' => array(
  316. 'ID' => 'DESC',
  317. ),
  318. ));
  319. if ($limit < 0)
  320. {
  321. $limit = 0;
  322. }
  323. $hasChanges = false;
  324. while ($item = $res->fetch())
  325. {
  326. $oldLimit = (int)($item['OPTIONS']['smtp']['limit'] ?? 0);
  327. if ($item['OPTIONS']['smtp'] && $limit !== $oldLimit)
  328. {
  329. $item['OPTIONS']['smtp']['limit'] = $limit;
  330. $updateResult = Internal\SenderTable::update($item['ID'], ['OPTIONS' => $item['OPTIONS']]);
  331. $hasChanges = true;
  332. if (!$quite && $limit < $oldLimit && $updateResult->isSuccess())
  333. {
  334. $event = new Event('main', self::MAIN_SENDER_SMTP_LIMIT_DECREASE, ['EMAIL'=>$email]);
  335. $event->send();
  336. }
  337. }
  338. }
  339. return $hasChanges;
  340. }
  341. /**
  342. * Remove limit from all connected senders.
  343. * @param string $email
  344. * @return bool
  345. * @throws Main\ArgumentException
  346. * @throws Main\ObjectPropertyException
  347. * @throws Main\SystemException
  348. */
  349. public static function removeEmailLimit(string $email): bool
  350. {
  351. $address = new \Bitrix\Main\Mail\Address($email);
  352. if (!$address->validate())
  353. {
  354. return false;
  355. }
  356. $email = $address->getEmail();
  357. $cache = new \CPHPCache();
  358. $cache->clean($email, '/main/mail/limit');
  359. $res = Internal\SenderTable::getList(array(
  360. 'filter' => array(
  361. 'IS_CONFIRMED' => true,
  362. '=EMAIL' => $email,
  363. ),
  364. 'order' => array(
  365. 'ID' => 'DESC',
  366. ),
  367. ));
  368. while ($item = $res->fetch())
  369. {
  370. if (isset($item['OPTIONS']['smtp']['limit']))
  371. {
  372. unset($item['OPTIONS']['smtp']['limit']);
  373. Internal\SenderTable::update($item['ID'], ['OPTIONS' => $item['OPTIONS']]);
  374. }
  375. }
  376. return true;
  377. }
  378. public static function applyCustomSmtp($event)
  379. {
  380. $headers = $event->getParameter('arguments')->additional_headers;
  381. $context = $event->getParameter('arguments')->context;
  382. if (empty($context) || !($context instanceof Context))
  383. {
  384. return;
  385. }
  386. if ($context->getSmtp() && $context->getSmtp()->getHost())
  387. {
  388. return;
  389. }
  390. if (preg_match('/X-Bitrix-Mail-SMTP-Host:/i', $headers))
  391. {
  392. return;
  393. }
  394. $eol = Mail::getMailEol();
  395. $eolh = preg_replace('/([a-f0-9]{2})/i', '\x\1', bin2hex($eol));
  396. if (preg_match(sprintf('/(^|%1$s)From:(.+?)(%1$s([^\s]|$)|$)/is', $eolh), $headers, $matches))
  397. {
  398. $address = new Address(preg_replace(sprintf('/%s\s+/', $eolh), '', $matches[2]));
  399. if ($address->validate())
  400. {
  401. if ($customSmtp = static::getCustomSmtp($address->getEmail()))
  402. {
  403. $context->setSmtp($customSmtp);
  404. }
  405. }
  406. }
  407. }
  408. public static function prepareUserMailboxes($userId = null)
  409. {
  410. global $USER;
  411. static $mailboxes = array();
  412. if (!($userId > 0))
  413. {
  414. if (is_object($USER) && $USER->isAuthorized())
  415. {
  416. $userId = $USER->getId();
  417. }
  418. }
  419. if (!($userId > 0))
  420. {
  421. return array();
  422. }
  423. if (array_key_exists($userId, $mailboxes))
  424. {
  425. return $mailboxes[$userId];
  426. }
  427. $mailboxes[$userId] = array();
  428. if (is_object($USER) && $USER->isAuthorized() && $USER->getId() == $userId)
  429. {
  430. $userData = array(
  431. 'ID' => $USER->getId(),
  432. 'TITLE' => $USER->getParam("TITLE"),
  433. 'NAME' => $USER->getFirstName(),
  434. 'SECOND_NAME' => $USER->getSecondName(),
  435. 'LAST_NAME' => $USER->getLastName(),
  436. 'LOGIN' => $USER->getLogin(),
  437. 'EMAIL' => $USER->getEmail(),
  438. );
  439. $isAdmin = in_array(1, $USER->getUserGroupArray());
  440. }
  441. else
  442. {
  443. $userData = Main\UserTable::getList(array(
  444. 'select' => array('ID', 'TITLE', 'NAME', 'SECOND_NAME', 'LAST_NAME', 'LOGIN', 'EMAIL'),
  445. 'filter' => array('=ID' => $userId),
  446. ))->fetch();
  447. $isAdmin = in_array(1, \CUser::getUserGroup($userId));
  448. }
  449. $userNameFormated = \CUser::formatName(\CSite::getNameFormat(), $userData, true, false);
  450. if (\CModule::includeModule('mail'))
  451. {
  452. foreach (\Bitrix\Mail\MailboxTable::getUserMailboxes($userId) as $mailbox)
  453. {
  454. if (!empty($mailbox['EMAIL']))
  455. {
  456. $mailboxName = trim($mailbox['USERNAME']) ?: trim($mailbox['OPTIONS']['name']) ?: $userNameFormated;
  457. $key = hash('crc32b', mb_strtolower($mailboxName).$mailbox['EMAIL']);
  458. $mailboxes[$userId][$key] = array(
  459. 'name' => $mailboxName,
  460. 'email' => $mailbox['EMAIL'],
  461. );
  462. }
  463. }
  464. }
  465. // @TODO: query
  466. $crmAddress = new Address(Main\Config\Option::get('crm', 'mail', ''));
  467. if ($crmAddress->validate())
  468. {
  469. $key = hash('crc32b', mb_strtolower($userNameFormated).$crmAddress->getEmail());
  470. $mailboxes[$userId][$key] = array(
  471. 'name' => $crmAddress->getName() ?: $userNameFormated,
  472. 'email' => $crmAddress->getEmail(),
  473. );
  474. }
  475. $res = SenderTable::getList(array(
  476. 'filter' => array(
  477. 'IS_CONFIRMED' => true,
  478. array(
  479. 'LOGIC' => 'OR',
  480. '=USER_ID' => $userId,
  481. 'IS_PUBLIC' => true,
  482. ),
  483. ),
  484. 'order' => array(
  485. 'ID' => 'ASC',
  486. ),
  487. ));
  488. while ($item = $res->fetch())
  489. {
  490. $item['NAME'] = trim($item['NAME']) ?: $userNameFormated;
  491. $item['EMAIL'] = mb_strtolower($item['EMAIL']);
  492. $key = hash('crc32b', mb_strtolower($item['NAME']).$item['EMAIL']);
  493. if (!isset($mailboxes[$userId][$key]))
  494. {
  495. $mailboxes[$userId][$key] = array(
  496. 'id' => $item['ID'],
  497. 'name' => $item['NAME'],
  498. 'email' => $item['EMAIL'],
  499. 'can_delete' => $userId == $item['USER_ID'] || $item['IS_PUBLIC'] && $isAdmin,
  500. );
  501. }
  502. else if (!isset($mailboxes[$userId][$key]['id']))
  503. {
  504. $mailboxes[$userId][$key]['id'] = $item['ID'];
  505. $mailboxes[$userId][$key]['can_delete'] = $userId == $item['USER_ID'] || $item['IS_PUBLIC'] && $isAdmin;
  506. }
  507. }
  508. foreach ($mailboxes[$userId] as $key => $item)
  509. {
  510. $mailboxes[$userId][$key]['formated'] = sprintf(
  511. $item['name'] ? '%s <%s>' : '%s%s',
  512. $item['name'], $item['email']
  513. );
  514. }
  515. $mailboxes[$userId] = array_values($mailboxes[$userId]);
  516. return $mailboxes[$userId];
  517. }
  518. }