Current Path : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/mail/lib/ |
Current File : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/mail/lib/helper.php |
<?php namespace Bitrix\Mail; use Bitrix\Main; class Helper { const SYNC_TIMEOUT = 300; public static function syncMailboxAgent($id) { $result = self::syncMailbox($id, $error); if ($result === false) return ''; return sprintf('Bitrix\Mail\Helper::syncMailboxAgent(%u);', $id); } public static function resyncDomainUsersAgent() { $res = MailServicesTable::getList(array( 'filter' => array( '=ACTIVE' => 'Y', '@SERVICE_TYPE' => array('domain', 'crdomain'), ) )); while ($item = $res->fetch()) { if ($item['SERVICE_TYPE'] == 'domain') { $lockName = sprintf('domain_users_sync_lock_%u', $item['ID']); $syncLock = \Bitrix\Main\Config\Option::get('mail', $lockName, 0); if ($syncLock < time()-3600) { \Bitrix\Main\Config\Option::set('mail', $lockName, time()); \CMailDomain2::getDomainUsers($item['TOKEN'], $item['SERVER'], $error, true); \Bitrix\Main\Config\Option::set('mail', $lockName, 0); } } else if ($item['SERVICE_TYPE'] == 'crdomain') { \CControllerClient::executeEvent('OnMailControllerResyncMemberUsers', array('DOMAIN' => $item['SERVER'])); } } return 'Bitrix\Mail\Helper::resyncDomainUsersAgent();'; } public static function syncMailbox($id, &$error) { global $DB; $error = null; $id = (int) $id; $mailbox = MailboxTable::getList(array( 'filter' => array('ID' => $id, 'ACTIVE' => 'Y'), 'select' => array('*', 'LANG_CHARSET' => 'SITE.CULTURE.CHARSET') ))->fetch(); if (empty($mailbox)) { $error = 'no mailbox'; return false; } if (!in_array($mailbox['SERVER_TYPE'], array('imap', 'controller', 'domain', 'crdomain'))) { $error = 'unsupported mailbox type'; return false; } if ($mailbox['SYNC_LOCK'] > time()-Helper::SYNC_TIMEOUT) return 0; if ($mailbox['USER_ID'] > 0) \CUserOptions::setOption('global', 'last_mail_sync_'.$mailbox['LID'], time(), false, $mailbox['USER_ID']); if (in_array($mailbox['SERVER_TYPE'], array('controller', 'crdomain'))) { // @TODO: request controller $result = \CMailDomain2::getImapData(); $mailbox['SERVER'] = $result['server']; $mailbox['PORT'] = $result['port']; $mailbox['USE_TLS'] = $result['secure']; } elseif ($mailbox['SERVER_TYPE'] == 'domain') { $result = \CMailDomain2::getImapData(); $mailbox['SERVER'] = $result['server']; $mailbox['PORT'] = $result['port']; $mailbox['USE_TLS'] = $result['secure']; } $mailbox['SYNC_LOCK'] = time(); $res = $DB->query(sprintf( 'UPDATE b_mail_mailbox SET SYNC_LOCK = %u WHERE ID = %u AND (SYNC_LOCK IS NULL OR SYNC_LOCK < %u)', $mailbox['SYNC_LOCK'], $id, $mailbox['SYNC_LOCK']-Helper::SYNC_TIMEOUT )); if (!$res->affectedRowsCount()) return 0; $result = static::syncImapMailbox($mailbox, $error); $success = $result !== false && empty($error); $DB->query(sprintf( 'UPDATE b_mail_mailbox SET SYNC_LOCK = 0 WHERE ID = %u AND SYNC_LOCK = %u', $id, $mailbox['SYNC_LOCK'] )); if ($mailbox['USER_ID'] > 0) { \CUserOptions::setOption('global', 'last_mail_check_'.$mailbox['LID'], time(), false, $mailbox['USER_ID']); \CUserOptions::setOption('global', 'last_mail_sync_'.$mailbox['LID'], time(), false, $mailbox['USER_ID']); \CUserOptions::setOption('global', 'last_mail_check_success_'.$mailbox['LID'], $success, false, $mailbox['USER_ID']); } else { \Bitrix\Main\Config\Option::set('mail', 'last_mail_check', time(), $mailbox['LID']); \Bitrix\Main\Config\Option::set('mail', 'last_mail_sync', time(), $mailbox['LID']); \Bitrix\Main\Config\Option::set('mail', 'last_mail_check_success', $success ? 'Y' : 'N', $mailbox['LID']); } return $result; } protected static function syncImapMailbox($mailbox, &$error) { $error = null; $count = 0; if (empty($mailbox['OPTIONS']['imap']) || !is_array($mailbox['OPTIONS']['imap'])) return false; $imapOptions = $mailbox['OPTIONS']['imap']; if (empty($imapOptions['income']) || !is_array($imapOptions['income'])) return false; $imapOptions['outcome'] = !empty($imapOptions['outcome']) && is_array($imapOptions['income']) ? array_diff($imapOptions['outcome'], $imapOptions['income']) : array(); $client = new Imap( $mailbox['SERVER'], $mailbox['PORT'], $mailbox['USE_TLS'] == 'Y' || $mailbox['USE_TLS'] == 'S', $mailbox['USE_TLS'] == 'Y', $mailbox['LOGIN'], $mailbox['PASSWORD'], $mailbox['LANG_CHARSET'] ); if (!$client->singin($error)) return 0; $localList = array(); $localSeen = array(); $res = MailMessageUidTable::getList(array( 'filter' => array('MAILBOX_ID' => $mailbox['ID']), 'select' => array('ID', 'HASH' => 'HEADER_MD5', 'IS_SEEN') )); while ($item = $res->fetch()) { $localList[$item['ID']] = $item['HASH']; $localSeen[$item['HASH']] = $item['IS_SEEN']; } $obsoleteList = $localList; $modifiedList = array(); $blacklist = array( 'domain' => array(), 'email' => array(), ); $res = BlacklistTable::getList(array( 'select' => array('ITEM_TYPE', 'ITEM_VALUE'), 'filter' => array( array( 'LOGIC' => 'OR', array( 'MAILBOX_ID' => $mailbox['ID'], ), array( 'MAILBOX_ID' => 0, 'SITE_ID' => $mailbox['LID'], ) ), ), )); while ($item = $res->fetch()) { if (Blacklist\ItemType::DOMAIN == $item['ITEM_TYPE']) $blacklist['domain'][] = $item['ITEM_VALUE']; else $blacklist['email'][] = $item['ITEM_VALUE']; } $domains = array(); $defaultDomain = \COption::getOptionString('main', 'server_name', ''); $res = Main\SiteTable::getList(array('select' => array('LID', 'SERVER_NAME'))); while ($site = $res->fetch()) { $domains[$site['LID']] = $site['SERVER_NAME'] ?: $defaultDomain; if (preg_match('/^(?<domain>.+):(?<port>\d+)$/', $domains[$site['LID']], $matches)) $domains[$site['LID']] = $matches['domain']; } if ($mailbox['USER_ID'] > 0) { $res = UserRelationsTable::getList(array('filter' => array('=USER_ID' => $mailbox['USER_ID'], '=ENTITY_ID' => null))); while ($relation = $res->fetch()) $blacklist['email'][] = sprintf('fwd%s@%s', $relation['TOKEN'], $domains[$relation['SITE_ID']]); } $blacklist['domain'] = array_map('strtolower', $blacklist['domain']); $blacklist['email'] = array_map('strtolower', $blacklist['email']); $nouidv = \Bitrix\Main\Config\Option::get('mail', sprintf('imap_mailbox_no_uidv_v4_%u', $mailbox['ID']), 'N'); $session = md5(uniqid('')); foreach (array_merge($imapOptions['income'], $imapOptions['outcome']) as $name) { $list = $client->listMessages($name, $uidtoken, $error); if ($list === false) // an error occurred { $obsoleteList = array(); continue; } if (empty($list)) continue; if (is_null($uidtoken) && $nouidv != 'Y') { addMessage2Log( sprintf( 'IMAP: UIDV not found v4 (%s:%s/%s)', $mailbox['SERVER'], $mailbox['PORT'], $mailbox['LOGIN'] ), 'mail', 0, false ); \Bitrix\Main\Config\Option::set('mail', sprintf('imap_mailbox_no_uidv_v4_%u', $mailbox['ID']), $nouidv = 'Y'); } foreach ($list as $item) { $skip = false; $item['seen'] = (bool) preg_match('/ ( ^ | \x20 ) \x5c ( Seen ) ( \x20 | $ ) /ix', $item['flags']); if (!is_null($item['uid'])) { $item['uid'] = md5(sprintf('%s:%u:%u', $name, $uidtoken, $item['uid'])); unset($obsoleteList[$item['uid']]); if (array_key_exists($item['uid'], $localList)) { $item['hash'] = $localList[$item['uid']]; $skip = true; } } if ($skip === false) { $header = $client->getMessage($name, $item['id'], 'header', $error); if ($header === false) // an error occurred { $obsoleteList = array(); $skip = true; } else { $item['hash'] = md5(sprintf('%s:%s:%u', trim($header), $item['date'], $item['size'])); if (is_null($item['uid'])) $item['uid'] = $item['hash']; if ($uid = array_search($item['hash'], $localList)) { unset($obsoleteList[$uid]); if ($uid != $item['hash']) { MailMessageUidTable::update( array('ID' => $uid, 'MAILBOX_ID' => $mailbox['ID']), array('ID' => $item['uid']) ); } $skip = true; } } } if ($skip === true) { if ($item['seen'] != in_array($localSeen[$item['hash']], array('Y', 'S'))) { if (in_array($localSeen[$item['hash']], array('S', 'U'))) { $item['seen'] = $localSeen[$item['hash']] == 'S'; $result = $client->updateMessageFlags($name, $item['id'], array( '\Seen' => $item['seen'], ), $err); if ($result !== false) { MailMessageUidTable::update( array('ID' => $item['uid'], 'MAILBOX_ID' => $mailbox['ID']), array('IS_SEEN' => $item['seen'] ? 'Y' : 'N') ); } } else { $modifiedList[$item['uid']] = array( 'hash' => $item['hash'], 'seen' => $item['seen'], ); } } continue; } if (\CMail::option('attachment_failure')) { if ($item['size'] > 200000) continue; } if ($mailbox['SYNC_LOCK'] < time()-Helper::SYNC_TIMEOUT*0.9) return $count; MailMessageUidTable::add(array( 'ID' => $item['uid'], 'MAILBOX_ID' => $mailbox['ID'], 'HEADER_MD5' => $item['hash'], 'IS_SEEN' => $item['seen'] ? 'Y' : 'N', 'SESSION_ID' => $session, 'DATE_INSERT' => new Main\Type\DateTime(), 'MESSAGE_ID' => 0, )); $localList[$item['uid']] = $item['hash']; if (!empty($mailbox['OPTIONS']['sync_from'])) { $syncFrom = (int) $mailbox['OPTIONS']['sync_from']; if (strtotime($item['date']) < $syncFrom) continue; } $item['outcome'] = in_array($name, $imapOptions['outcome']); $parsedHeader = \CMailMessage::parseHeader($header, $mailbox['LANG_CHARSET']); $parsedFrom = array_unique(array_map('strtolower', array_filter(array_merge( \CMailUtil::extractAllMailAddresses($parsedHeader->getHeader('FROM')), \CMailUtil::extractAllMailAddresses($parsedHeader->getHeader('REPLY-TO')) ), 'trim'))); $parsedTo = array_unique(array_map('strtolower', array_filter(array_merge( \CMailUtil::extractAllMailAddresses($parsedHeader->getHeader('TO')), \CMailUtil::extractAllMailAddresses($parsedHeader->getHeader('CC')), \CMailUtil::extractAllMailAddresses($parsedHeader->getHeader('BCC')), \CMailUtil::extractAllMailAddresses($parsedHeader->getHeader('X-Original-Rcpt-to')) ), 'trim'))); if (!empty($blacklist['email'])) { if (!$item['outcome'] && array_intersect($parsedFrom, $blacklist['email'])) continue; if ($item['outcome'] && !array_diff($parsedTo, $blacklist['email'])) continue; } if (!empty($blacklist['domain'])) { $skip = false; $haystack = $item['outcome'] ? $parsedTo : $parsedFrom; foreach ($haystack as $email) { $domain = substr($email, strrpos($email, '@')); if ($domain != $email) { if (in_array($domain, $blacklist['domain'])) { $skip = true; if (!$item['outcome']) break; } else { $skip = false; if ($item['outcome']) break; } } } if ($skip) continue; } $messageId = 0; $body = $client->getMessage($name, $item['id'], null, $error); if ($body !== false) { if (!preg_match('/\r\n$/', $body)) $body .= "\r\n"; $messageId = \CMailMessage::addMessage( $mailbox['ID'], $body, $mailbox['CHARSET'] ?: $mailbox['LANG_CHARSET'], array( 'outcome' => $item['outcome'], 'seen' => $item['seen'], 'hash' => $item['hash'], ) ); } if ($messageId > 0) { $count++; MailMessageUidTable::update( array('ID' => $item['uid'], 'MAILBOX_ID' => $mailbox['ID']), array('MESSAGE_ID' => $messageId) ); } else { MailMessageUidTable::delete( array('ID' => $item['uid'], 'MAILBOX_ID' => $mailbox['ID']) ); } } } if (!empty($obsoleteList)) { foreach ($obsoleteList as $msgUid => $dummy) { MailMessageUidTable::delete(array( 'ID' => $msgUid, 'MAILBOX_ID' => $mailbox['ID'] )); } foreach ($obsoleteList as $msgHash) { $event = new Main\Event( 'mail', 'OnMessageObsolete', array( 'user' => $mailbox['USER_ID'], 'hash' => $msgHash, ) ); $event->send(); } } if (!empty($modifiedList)) { foreach ($modifiedList as $msgUid => $msgData) { MailMessageUidTable::update( array('ID' => $msgUid, 'MAILBOX_ID' => $mailbox['ID']), array('IS_SEEN' => $msgData['seen'] ? 'Y' : 'N') ); } foreach ($modifiedList as $msgData) { $event = new Main\Event( 'mail', 'OnMessageModified', array( 'user' => $mailbox['USER_ID'], 'hash' => $msgData['hash'], 'seen' => $msgData['seen'], ) ); $event->send(); } } return $count; } public static function listImapDirs($mailbox, &$error, &$errors = null) { $error = null; $errors = null; $client = new Imap( $mailbox['SERVER'], $mailbox['PORT'], $mailbox['USE_TLS'] == 'Y' || $mailbox['USE_TLS'] == 'S', $mailbox['USE_TLS'] == 'Y', $mailbox['LOGIN'], $mailbox['PASSWORD'], LANG_CHARSET ); $list = $client->listMailboxes('*', $error); $errors = $client->getErrors(); if ($list === false) return false; $flat = function($list, $prefix = '', $level = 0) use (&$flat) { $k = count($list); for ($i = 0; $i < $k; $i++) { $item = $list[$i]; $list[$i] = array( 'level' => $level, 'name' => preg_replace(sprintf('/^%s/', preg_quote($prefix, '/')), '', $item['name']), 'path' => $item['name'] ); if (preg_match('/ ( ^ | \x20 ) \x5c Noselect ( \x20 | $ ) /ix', $item['flags'])) { $list[$i]['disabled'] = true; } else { if (strtolower($item['name']) == 'inbox') $list[$i]['income'] = true; if (preg_match('/ ( ^ | \x20 ) \x5c Sent ( \x20 | $ ) /ix', $item['flags'])) $list[$i]['outcome'] = true; } if (!empty($item['children'])) { $children = $flat($item['children'], $item['name'].$item['delim'], $level+1); array_splice($list, $i+1, 0, $children); $i += count($children); $k += count($children); } } return $list; }; return $flat($list); } public static function getImapUnseen($mailbox, $dir = 'inbox', &$error, &$errors = null) { $error = null; $errors = null; $client = new Imap( $mailbox['SERVER'], $mailbox['PORT'], $mailbox['USE_TLS'] == 'Y' || $mailbox['USE_TLS'] == 'S', $mailbox['USE_TLS'] == 'Y', $mailbox['LOGIN'], $mailbox['PASSWORD'], LANG_CHARSET ); $result = $client->getUnseen($dir, $error); $errors = $client->getErrors(); return $result; } public static function addImapMessage($id, $data, &$error) { $error = null; $id = (int) (is_array($id) ? $id['ID'] : $id); $mailbox = MailboxTable::getList(array( 'filter' => array('ID' => $id, 'ACTIVE' => 'Y'), 'select' => array('*', 'LANG_CHARSET' => 'SITE.CULTURE.CHARSET') ))->fetch(); if (empty($mailbox)) return; if (!in_array($mailbox['SERVER_TYPE'], array('imap', 'controller', 'domain', 'crdomain'))) return; if (in_array($mailbox['SERVER_TYPE'], array('controller', 'crdomain'))) { // @TODO: request controller $result = \CMailDomain2::getImapData(); $mailbox['SERVER'] = $result['server']; $mailbox['PORT'] = $result['port']; $mailbox['USE_TLS'] = $result['secure']; } elseif ($mailbox['SERVER_TYPE'] == 'domain') { $result = \CMailDomain2::getImapData(); $mailbox['SERVER'] = $result['server']; $mailbox['PORT'] = $result['port']; $mailbox['USE_TLS'] = $result['secure']; } $client = new Imap( $mailbox['SERVER'], $mailbox['PORT'], $mailbox['USE_TLS'] == 'Y' || $mailbox['USE_TLS'] == 'S', $mailbox['USE_TLS'] == 'Y', $mailbox['LOGIN'], $mailbox['PASSWORD'], $mailbox['LANG_CHARSET'] ?: $mailbox['CHARSET'] ); $imapOptions = $mailbox['OPTIONS']['imap']; if (empty($imapOptions['outcome']) || !is_array($imapOptions['outcome'])) return; return $client->addMessage(reset($imapOptions['outcome']), $data, $error); } public static function updateImapMessage($userId, $hash, $data, &$error) { $error = null; $res = MailMessageUidTable::getList(array( 'select' => array('ID', 'MAILBOX_ID', 'IS_SEEN'), 'filter' => array( '=HEADER_MD5' => $hash, 'MAILBOX.USER_ID' => array($userId, 0), ), )); while ($msgUid = $res->fetch()) { if (in_array($msgUid['IS_SEEN'], array('Y', 'S')) != $data['seen']) { MailMessageUidTable::update( array('ID' => $msgUid['ID'], 'MAILBOX_ID' => $msgUid['MAILBOX_ID']), array('IS_SEEN' => $data['seen'] ? 'S' : 'U') ); } } } } class DummyMail extends Main\Mail\Mail { public function initSettings() { parent::initSettings(); $this->settingServerMsSmtp = false; $this->settingMailFillToEmail = false; $this->settingMailConvertMailHeader = true; $this->settingConvertNewLineUnixToWindows = true; } public static function getMailEol() { return "\r\n"; } public function __toString() { return sprintf("%s\r\n\r\n%s", $this->getHeaders(), $this->getBody()); } }