30 if(isset(self::MAXIMUM_SYNCHRONIZATION_LENGTHS_OF_INTERVALS[$num]))
32 return self::MAXIMUM_SYNCHRONIZATION_LENGTHS_OF_INTERVALS[$num];
36 return self::MAXIMUM_SYNCHRONIZATION_LENGTHS_OF_INTERVALS[
count(self::MAXIMUM_SYNCHRONIZATION_LENGTHS_OF_INTERVALS)-1];
60 if (!empty($this->syncParams[
'currentDir']))
62 $currentDir = $this->syncParams[
'currentDir'];
67 $currentSyncDir = $this->
getDirsHelper()->getDirByPath($currentSyncDirPath);
69 if ($totalSyncDirs > 0 && $currentSyncDir !=
null)
71 $currentSyncDirMessages = Mail\MailMessageUidTable::getList([
76 '=MAILBOX_ID' => $this->mailbox[
'ID'],
77 '=DIR_MD5' => $currentSyncDir->getDirMd5(),
82 $currentSyncDirMessagesCount = (int)$currentSyncDirMessages[
'TOTAL'];
83 $currentSyncDirMessagesAll = (int)$currentSyncDir->getMessageCount();
84 $currentSyncDirPosition = $this->
getDirsHelper()->getCurrentSyncDirPositionByDefault(
85 $currentSyncDir->getPath(),
89 if ($currentDir !=
null) {
93 if ($currentSyncDirMessagesAll <= 0)
95 $progress = ($currentSyncDirPosition + 1) / $totalSyncDirs;
99 $progress = ($currentSyncDirMessagesCount / $currentSyncDirMessagesAll + $currentSyncDirPosition) / $totalSyncDirs;
106 return parent::getSyncStatus();
112 if (!empty($this->syncParams[
'currentDir']))
114 $currentSyncDir = $this->
getDirsHelper()->getDirByPath($this->syncParams[
'currentDir']);
117 if (!empty($currentSyncDir))
119 $currentSyncDirMessages = Mail\MailMessageUidTable::getList([
124 '=MAILBOX_ID' => $this->mailbox[
'ID'],
125 '=DIR_MD5' => $currentSyncDir->getDirMd5(),
126 '==DELETE_TIME' => 0,
130 $currentSyncDirMessagesCount = (int) $currentSyncDirMessages[
'TOTAL'];
131 $currentSyncDirMessagesAll = (int) $currentSyncDir->getMessageCount();
133 if ($currentSyncDirMessagesAll > 0)
135 return ($currentSyncDirMessagesCount / $currentSyncDirMessagesAll);
155 $chunks = array_chunk($UIDs, 5);
157 $existingMessage = NULL;
159 foreach ($chunks as $chunk)
174 if(!isset($item[
'FLAGS']))
179 $messageDeleted = preg_grep(
'/^ \x5c Deleted $/ix', $item[
'FLAGS']) ? true :
false;
183 $existingMessage = $item;
190 if(!is_null($existingMessage))
192 if(isset($existingMessage[
'UID']))
194 return $existingMessage[
'UID'];
204 $mailboxID = $this->mailbox[
'ID'];
206 $syncDirs = $directoryHelper->getSyncDirs();
208 $numberOfUnSynchronizedDirs =
count($syncDirs);
210 foreach ($syncDirs as
$dir)
212 $dirPath =
$dir->getPath();
213 $dirId =
$dir->getId();
215 $internalDate = \Bitrix\Mail\Helper::getLastDeletedOldMessageInternaldate($mailboxID, $dirPath);
218 'MAILBOX_ID' => $mailboxID,
219 'ENTITY_TYPE' =>
'DIR',
220 'ENTITY_ID' => $dirId,
221 'PROPERTY_NAME' =>
'SYNC_IS_OLD_STATUS',
225 '=MAILBOX_ID' => $keyRow[
'MAILBOX_ID'],
226 '=ENTITY_TYPE' => $keyRow[
'ENTITY_TYPE'],
227 '=ENTITY_ID' => $keyRow[
'ENTITY_ID'],
228 '=PROPERTY_NAME' => $keyRow[
'PROPERTY_NAME'],
231 $startValue =
'started_for_date_'.$internalDate;
240 ])->fetchAll()[0][
'VALUE'] !==
'completed')
244 [
'VALUE' => $startValue]
249 if($synchronizationSuccess)
253 [
'VALUE' =>
'completed']
255 $numberOfUnSynchronizedDirs--;
260 $numberOfUnSynchronizedDirs--;
266 $fields[
'VALUE'] = $startValue;
273 if($synchronizationSuccess)
277 [
'VALUE' =>
'completed']
279 $numberOfUnSynchronizedDirs--;
284 if($numberOfUnSynchronizedDirs === 0)
296 $mailboxID = $this->mailbox[
'ID'];
298 $syncDirs = $directoryHelper->getSyncDirs();
300 $numberOfUnSynchronizedDirs =
count($syncDirs);
302 foreach ($syncDirs as
$dir)
304 $dirPath =
$dir->getPath();
305 $dirId =
$dir->getId();
310 'MAILBOX_ID' => $mailboxID,
311 'ENTITY_TYPE' =>
'DIR',
312 'ENTITY_ID' => $dirId,
313 'PROPERTY_NAME' =>
'SYNC_FIRST_DAY',
317 '=MAILBOX_ID' => $keyRow[
'MAILBOX_ID'],
318 '=ENTITY_TYPE' => $keyRow[
'ENTITY_TYPE'],
319 '=ENTITY_ID' => $keyRow[
'ENTITY_ID'],
320 '=PROPERTY_NAME' => $keyRow[
'PROPERTY_NAME'],
323 $startValue =
'started_for_date_'.$internalDate;
332 ])->fetchAll()[0][
'VALUE'] !==
'completed')
336 [
'VALUE' => $startValue]
339 \CTimeZone::Disable();
341 \CTimeZone::Enable();
343 if($synchronizationSuccess)
347 [
'VALUE' =>
'completed']
349 $numberOfUnSynchronizedDirs--;
354 $numberOfUnSynchronizedDirs--;
360 $fields[
'VALUE'] = $startValue;
365 \CTimeZone::Disable();
367 \CTimeZone::Enable();
369 if($synchronizationSuccess)
373 [
'VALUE' =>
'completed']
375 $numberOfUnSynchronizedDirs--;
380 if($numberOfUnSynchronizedDirs === 0)
392 $syncReport = $this->syncMailbox();
393 if (
false === $syncReport[
'syncCount'])
403 $dirPath = $this->
getDirsHelper()->getOutcomePath() ?:
'INBOX';
408 'DIR_MD5' => md5($dirPath),
421 parent::syncOutgoing();
426 $dirPath = $this->
getDirsHelper()->getOutcomePath() ?:
'INBOX';
437 if (!empty($excerpt[
'__unique_headers']))
439 if ($this->client->searchByHeader(
false, $dirPath, $excerpt[
'__unique_headers'],
$error))
445 if (!empty($excerpt[
'ID']))
447 class_exists(
'Bitrix\Mail\Helper');
452 'X-Bitrix-Mail-Message-UID' => $excerpt[
'ID'],
457 $result = $this->client->append(
484 if (empty($excerpt[
'MSG_UID']) || empty($excerpt[
'DIR_MD5']))
489 $dirPath = $this->
getDirsHelper()->getDirPathByHash($excerpt[
'DIR_MD5']);
495 $body = $this->client->fetch(
true, $dirPath, $excerpt[
'MSG_UID'],
'(BODY.PEEK[])',
$error);
504 return empty($body[
'BODY[]']) ? null : $body[
'BODY[]'];
509 if (empty($excerpt[
'MSG_UID']) || empty($excerpt[
'DIR_MD5']))
514 $dirPath = $this->
getDirsHelper()->getDirPathByHash($excerpt[
'DIR_MD5']);
520 $rfc822Parts =
array();
523 $bodystructure->traverse(
526 if ($item->isMultipart())
531 $isTextItem = $item->isBodyText();
535 if (
'message' === $item->getType() &&
'rfc822' === $item->getSubtype())
537 $rfc822Parts[] = $item;
539 return sprintf(
'BODY.PEEK[%1$s.HEADER] BODY.PEEK[%1$s.TEXT]', $item->getNumber());
542 return sprintf(
'BODY.PEEK[%1$s.MIME] BODY.PEEK[%1$s]', $item->getNumber());
554 $parts = $this->client->fetch(
558 sprintf(
'(%s)', join(
' ',
$select)),
562 if (
false === $parts)
569 foreach ($rfc822Parts as $item)
571 $headerKey = sprintf(
'BODY[%s.HEADER]', $item->getNumber());
572 $bodyKey = sprintf(
'BODY[%s.TEXT]', $item->getNumber());
574 if (array_key_exists($headerKey, $parts) || array_key_exists($bodyKey, $parts))
576 $partMime =
'Content-Type: message/rfc822';
577 if (!empty($item->getParams()[
'name']))
579 $partMime .= sprintf(
'; name="%s"', $item->getParams()[
'name']);
582 if (!empty($item->getDisposition()[0]))
584 $partMime .= sprintf(
"\r\nContent-Disposition: %s", $item->getDisposition()[0]);
585 if (!empty($item->getDisposition()[1]) && is_array($item->getDisposition()[1]))
587 foreach ($item->getDisposition()[1] as
$name => $value)
589 $partMime .= sprintf(
'; %s="%s"',
$name, $value);
594 $parts[sprintf(
'BODY[%1$s.MIME]', $item->getNumber())] = $partMime;
595 $parts[sprintf(
'BODY[%1$s]', $item->getNumber())] = sprintf(
597 rtrim($parts[$headerKey],
"\r\n"),
598 ltrim($parts[$bodyKey],
"\r\n")
601 unset($parts[$headerKey], $parts[$bodyKey]);
610 static $lastCacheSession;
612 if ($this->session === $lastCacheSession)
617 $dirs = $this->client->listex(
'',
'%',
$error);
626 foreach ($dirs as $item)
628 $parts = explode($item[
'delim'], $item[
'name']);
630 $item[
'path'] = $item[
'name'];
631 $item[
'name'] = end($parts);
633 $list[$item[
'name']] = $item;
638 $lastCacheSession = $this->session;
653 foreach ($dirs as
$dir)
655 $parts = explode(
$dir[
'delim'],
$dir[
'name']);
658 $dir[
'name'] = end($parts);
682 $data[$folderFrom][] = $id;
691 foreach (
$result->getData() as $folderFrom => $ids)
693 $result = $this->client->unseen($ids, $folderFrom);
694 if (!
$result->isSuccess() || !$this->client->getErrors()->isEmpty())
705 foreach (
$result->getData() as $folderFrom => $ids)
707 $result = $this->client->seen($ids, $folderFrom);
708 if (!
$result->isSuccess() || !$this->client->getErrors()->isEmpty())
720 foreach (
$result->getData() as $folderFrom => $ids)
722 $moveResult = $this->client->moveMails($ids, $folderFrom, $folderTo);
723 if (!$moveResult->isSuccess() || !$this->client->getErrors()->isEmpty())
744 public function syncMailbox()
746 if (!$this->client->authenticate(
$error))
754 'reSyncStatus' =>
false,
761 if (!empty($this->syncParams[
'currentDir']))
763 $currentDir = $this->syncParams[
'currentDir'];
766 $dirsSync = $this->getDirsHelper()->getSyncDirsOrderByTime($currentDir);
768 if (empty($dirsSync))
773 $lastDir = $this->getDirsHelper()->getLastSyncDirByDefault($currentDir);
776 foreach ($dirsSync as $item)
780 $syncReport[
'syncCount'] += $this->syncDir($item->getPath());
782 if ($this->isTimeQuotaExceeded())
789 if ($lastDir !=
null && $item->getPath() == $lastDir->getPath())
811 '!@DIR_MD5' => array_map(
817 'info' =>
'disabled directory synchronization in Bitrix',
824 $this->lastSyncResult[
'deletedMessages'] += $countDeleted;
826 if (!empty($this->syncParams[
'full']))
828 foreach ($dirsSync as $item)
830 $reSyncReport = $this->
resyncDir($item->getPath());
832 if($reSyncReport[
'complete'])
834 $syncReport[
'reSyncCount']++;
842 if($syncReport[
'reSyncCount'] ===
count($dirsSync))
844 $syncReport[
'reSyncStatus'] =
true;
861 if (
$dir->isSyncLock() || !
$dir->startSyncLock())
868 $dir->stopSyncLock();
870 $this->lastSyncResult[
'newMessages'] +=
$result;
871 if (!
$dir->isTrash() && !
$dir->isSpam() && !
$dir->isDraft() && !
$dir->isOutcome())
873 $this->lastSyncResult[
'newMessagesNotify'] +=
$result;
881 if($internalDate ===
false)
889 $entity = \Bitrix\Mail\MailMessageUidTable::getEntity();
897 '<=INTERNALDATE' => $internalDate,
898 '=DIR_MD5' =>
$dir->getDirMd5(),
899 '=MAILBOX_ID' => $mailboxId,
906 'UPDATE %s SET IS_OLD = "Y", IS_SEEN = "Y" WHERE %s LIMIT 1000',
929 public function syncMessages($mailboxID, $dirPath, $UIDs, $isRecovered =
false)
931 $meta = $this->client->select($dirPath,
$error);
932 $uidtoken = $meta[
'uidvalidity'];
944 $chunks = array_chunk($UIDs, 10);
946 $entity = Mail\MailMessageUidTable::getEntity();
949 foreach ($chunks as $chunk)
952 'DELETE FROM %s WHERE %s',
957 '@MSG_UID' => $chunk,
959 '=MAILBOX_ID' => $mailboxID,
960 '=DIR_MD5' =>
$dir->getDirMd5()
969 '(UID FLAGS INTERNALDATE RFC822.SIZE BODYSTRUCTURE BODY.PEEK[HEADER])',
978 $this->warnings->add($this->client->getErrors()->toArray());
1001 if(empty($item[
'__replaces']))
1005 if($outgoingMessageId !==
'')
1007 $item[
'__replaces'] = $outgoingMessageId;
1013 $this->
syncMessage(
$dir->getPath(), $item, $hashesMap,
true, $isOutgoing, $isRecovered);
1025 public function isAuthenticated(): bool
1027 $dirs = $this->getDirsHelper()->getSyncDirsOrderByTime();
1035 foreach ($dirs as
$dir)
1037 if (\
Bitrix\
Mail\Helper::getImapUnseen($this->mailbox,
$dir->getPath()) !==
false)
1048 if($internalDate ===
false)
1053 $mailboxID = $this->mailbox[
'ID'];
1055 $UIDsOnService = \Bitrix\Mail\Helper::getImapUIDsForSpecificDay($mailboxID, $dirPath, $internalDate);
1057 return $this->
syncMessages($mailboxID, $dirPath, $UIDsOnService);
1062 $messagesSynced = 0;
1065 $meta = $this->client->select(
$dir->getPath(),
$error);
1067 if (
false === $meta)
1069 $this->warnings->add($this->client->getErrors()->toArray());
1071 if ($this->client->isExistsDir(
$dir->getPath(),
$error) ===
false)
1081 $intervalSynchronizationAttempts = 0;
1083 while ($range = $this->
getSyncRange(
$dir->getPath(), $uidtoken, $intervalSynchronizationAttempts))
1085 $syncDown = $range[0] > $range[1];
1089 MessageInternalDateHandler::clearStartInternalDate(
$dir->getMailboxId(),
$dir->getDirMd5());
1098 '(UID FLAGS INTERNALDATE RFC822.SIZE BODYSTRUCTURE BODY.PEEK[HEADER])',
1102 $fetchErrors=$this->client->getErrors();
1103 $errorReceivingMessages = $fetchErrors->getErrorByCode(210) !==
null;
1104 $failureDueToDataVolume = $fetchErrors->getErrorByCode(104) !==
null;
1110 if($errorReceivingMessages && !$failureDueToDataVolume)
1115 return $messagesSynced;
1117 elseif($failureDueToDataVolume && $intervalSynchronizationAttempts <
count(self::MAXIMUM_SYNCHRONIZATION_LENGTHS_OF_INTERVALS) - 1 )
1119 $intervalSynchronizationAttempts++;
1130 $this->warnings->add($fetchErrors->toArray());
1138 $intervalSynchronizationAttempts = 0;
1159 $numberOfMessagesInABatch = 1;
1160 $numberLeftToFillTheBatch = $numberOfMessagesInABatch;
1164 $isOutgoing =
false;
1166 if(empty($item[
'__replaces']))
1170 if($outgoingMessageId !==
'')
1172 $item[
'__replaces'] = $outgoingMessageId;
1177 if ($this->
syncMessage(
$dir->getPath(), $item, $hashesMap,
false, $isOutgoing))
1179 $this->lastSyncResult[
'newMessageId'] = end($hashesMap);
1182 $numberLeftToFillTheBatch--;
1183 if($numberLeftToFillTheBatch === 0 and
Main\Loader::includeModule(
'pull'))
1185 $numberOfMessagesInABatch *= 2;
1186 $numberLeftToFillTheBatch = $numberOfMessagesInABatch;
1187 \CPullWatch::addToStack(
1188 'mail_mailbox_' . $this->mailbox[
'ID'],
1191 'dir' =>
$dir->getPath(),
1192 'mailboxId' => $this->mailbox[
'ID'],
1194 'module_id' =>
'mail',
1195 'command' =>
'new_message_is_synchronized',
1209 if (
false === $range)
1211 $this->warnings->add($this->client->getErrors()->toArray());
1216 return $messagesSynced;
1219 public function resyncDir($dirPath, $numberForResync =
false)
1229 'complete' =>
false,
1230 'dir' =>
$dir->getPath(),
1231 'updated' => -$this->lastSyncResult[
'updatedMessages'],
1232 'deleted' => -$this->lastSyncResult[
'deletedMessages'],
1237 $report[
'updated'] += $this->lastSyncResult[
'updatedMessages'];
1238 $report[
'deleted'] += $this->lastSyncResult[
'deletedMessages'];
1242 $report[
'errors'] = $this->client->getErrors()->toArray();
1248 $report[
'errors'] = [
1249 'isTimeQuotaExceeded'
1254 $report[
'complete'] =
true;
1263 static $borderlineUIDs =
null;
1265 if (!is_null($borderlineUIDs))
1267 return $borderlineUIDs;
1271 $meta = $this->client->select(
$dir->getPath(),
$error);
1273 if (!isset($meta[
'exists']))
1275 $this->warnings->add($this->client->getErrors()->toArray());
1276 $borderlineUIDs = [];
1278 return $borderlineUIDs;
1281 $messagesNumberInTheMailService = $meta[
'exists'];
1282 $messages = $this->fetchMessage(sprintf(
'1,%u', $messagesNumberInTheMailService),
$dir->getPath());
1286 $borderlineUIDs = [];
1288 return $borderlineUIDs;
1299 if($range[0] === $range[1] && $messagesNumberInTheMailService > 1)
1301 $borderlineUIDs = [];
1303 return $borderlineUIDs;
1306 $borderlineUIDs = $range;
1308 return $borderlineUIDs;
1313 $borderlineUIDs = $this->getBorderlineUIDs(
$dir);
1315 if (empty($borderlineUIDs))
1321 '=DIR_MD5' =>
$dir->getDirMd5(),
1324 '>=MSG_UID' => $borderlineUIDs[0],
1325 '<=MSG_UID' => $borderlineUIDs[1],
1342 private function fetchMessage(
string $format,
string $dirPath):
array
1345 $messages = $this->client->fetch(
false, $dirPath, $format,
'(UID FLAGS)',
$error);
1351 $this->warnings->add($this->client->getErrors()->toArray());
1365 $meta = $this->client->select(
$dir->getPath(),
$error);
1367 if (!isset($meta[
'exists']))
1369 $this->warnings->add($this->client->getErrors()->toArray());
1374 $uidtoken = $meta[
'uidvalidity'];
1376 if ($meta[
'exists'] > 0)
1382 '=DIR_MD5' => md5(
$dir->getPath(
true)),
1383 '<DIR_UIDV' => $uidtoken,
1386 'info' =>
'the directory has been deleted',
1392 $this->lastSyncResult[
'deletedMessages'] += $countDeleted;
1397 if ($this->client->ensureEmpty(
$dir->getPath(),
$error))
1401 '=DIR_MD5' => md5(
$dir->getPath(
true)),
1404 'info' =>
'all messages in the directory have been deleted ',
1410 $this->lastSyncResult[
'deletedMessages'] += $countDeleted;
1416 $messagesNumberInTheMailService = $meta[
'exists'];
1417 $messages = $this->fetchMessage((($messagesNumberInTheMailService > 10000 || $numberForResync !==
false) ? sprintf(
'1,%u', $messagesNumberInTheMailService) :
'1:*'),
$dir->getPath());
1421 return (
false ===
$messages ?
false :
null);
1432 if($range[0] === $range[1] && $messagesNumberInTheMailService > 1)
1440 '=DIR_MD5' => md5(
$dir->getPath(
true)),
1444 '<MSG_UID' => $range[0],
1445 '>MSG_UID' => $range[1],
1449 'info' =>
'optimized deletion of non-existent messages',
1455 $this->lastSyncResult[
'deletedMessages'] += $countDeleted;
1458 if($numberForResync !==
false)
1460 $range1 = $meta[
'exists'];
1461 $range0 = max($range1 - ($numberForResync - 1), 1);
1462 $messages = $this->fetchMessage(sprintf(
'%u:%u', $range0, $range1),
$dir->getPath());
1474 if (!($meta[
'exists'] > 10000))
1481 $range1 = $meta[
'exists'];
1484 $rangeSize = $range1 > 10000 ? 8000 : $range1;
1485 $range0 = max($range1 - $rangeSize, 1);
1487 $messages = $this->fetchMessage(sprintf(
'%u:%u', $range0, $range1),
$dir->getPath());
1501 $range1 -= $rangeSize;
1510 $messages[$id][
'__from'] = array_unique(array_map(
1523 protected function blacklistMessages($dirPath, &
$messages)
1525 $trashDir = $this->getDirsHelper()->getTrashPath();
1526 $spamDir = $this->getDirsHelper()->getSpamPath();
1528 $targetDir = $spamDir ?: $trashDir ?:
null;
1529 $dir = $this->getDirsHelper()->getDirByPath($dirPath);
1531 if (empty($targetDir) || (
$dir && (
$dir->isTrash() ||
$dir->isSpam())))
1538 'domain' =>
array(),
1541 $blacklistEmails = Mail\BlacklistTable::query()
1544 '=SITE_ID' => $this->mailbox[
'LID'],
1547 '=MAILBOX_ID' => $this->mailbox[
'ID'],
1550 '@USER_ID' =>
array(0, $this->mailbox[
'USER_ID']),
1555 ->fetchCollection();
1556 foreach ($blacklistEmails as $blacklistEmail)
1558 if ($blacklistEmail->isDomainType())
1560 $blacklist[
'domain'][] = $blacklistEmail;
1564 $blacklist[
'email'][] = $blacklistEmail;
1568 if (empty($blacklist[
'email']) && empty($blacklist[
'domain']))
1573 $targetMessages = [];
1574 $emailAddresses = array_map(
function ($element)
1577 return $element->getItemValue();
1578 }, $blacklist[
'email']);
1579 $domains = array_map(
function ($element)
1582 return $element->getItemValue();
1583 }, $blacklist[
'domain']);
1587 if (!empty($blacklist[
'email']))
1589 if (array_intersect(
$messages[$id][
'__from'], $emailAddresses))
1591 $targetMessages[$id] = $item[
'UID'];
1597 foreach ($blacklist[
'email'] as $blacklistMail)
1600 if (array_intersect(
$messages[$id][
'__from'], [$blacklistMail->convertDomainToPunycode()]))
1602 $targetMessages[$id] = $item[
'UID'];
1609 if (!empty($blacklist[
'domain']))
1614 if (in_array($domain, $domains))
1616 $targetMessages[$id] = $item[
'UID'];
1624 if (!empty($targetMessages))
1626 if ($this->client->moveMails($targetMessages, $dirPath, $targetDir)->isSuccess())
1635 return md5(sprintf(
'%s:%u:%u', $dirPath, $uidToken, $UID));
1651 $existingMessagesId = [];
1664 '=DIR_MD5' => md5(Emoji::encode($dirPath)),
1665 '=DIR_UIDV' => $uidToken,
1666 '>=MSG_UID' => $range[0],
1667 '<=MSG_UID' => $range[1],
1671 while ($item =
$result->fetch())
1673 $existingMessagesId[] = $item[
'ID'];
1680 if (in_array($messageUid, $existingMessagesId))
1687 $existingMessagesId[] = $messageUid;
1694 'select' => [
'HEADER_MD5',
'MESSAGE_ID',
'DATE_INSERT'],
1696 '@HEADER_MD5' => $headerHashes,
1704 'select' =>
array(
'ID',
'MESSAGE_ID',
'DATE_INSERT'),
1706 '@ID' => array_values($idsForDataBase),
1714 $idsForDataBase = [];
1718 $hashes[$id] = $item[
'__fields'][
'HEADER_MD5'];
1719 $idsForDataBase[$id] = $item[
'__fields'][
'ID'];
1724 foreach ($hashes as $id =>
$hash)
1726 if (!array_key_exists(
$hash, $hashesMap))
1728 $hashesMap[
$hash] = [];
1731 $hashesMap[
$hash][] = $id;
1741 while ($item = $existingMessages->fetch())
1743 foreach ((
array)$hashesMap[$item[
'HEADER_MD5']] as $id)
1745 $messages[$id][
'__created'] = $item[
'DATE_INSERT'];
1746 $messages[$id][
'__fields'][
'MESSAGE_ID'] = $item[
'MESSAGE_ID'];
1756 while ($item = $existingMessages->fetch())
1758 $id = array_search($item[
'ID'], $idsForDataBase);
1759 $messages[$id][
'__created'] = $item[
'DATE_INSERT'];
1760 $messages[$id][
'__fields'][
'MESSAGE_ID'] = $item[
'MESSAGE_ID'];
1761 $messages[$id][
'__replaces'] = $item[
'ID'];
1768 \DateTime::createFromFormat(
1770 ltrim(trim(
$message[
'INTERNALDATE']),
'0')
1776 'DIR_MD5' => md5(Emoji::encode($dirPath)),
1777 'DIR_UIDV' => $uidToken,
1779 'INTERNALDATE' =>
$message[
'__internaldate'],
1780 'IS_SEEN' => (isset(
$message[
'FLAGS']) && preg_grep(
'/^ \x5c Seen $/ix',
$message[
'FLAGS'])) ?
'Y' :
'N',
1788 if (preg_match(
'/X-Bitrix-Mail-Message-UID:\s*([a-f0-9]+)/i',
$message[
'BODY[HEADER]'],
$matches))
1807 'select' =>
array(
'ID',
'MESSAGE_ID',
'IS_SEEN'),
1809 '=DIR_MD5' => md5($dirPath),
1810 '=DIR_UIDV' => $uidtoken,
1811 '>=MSG_UID' => $range[0],
1812 '<=MSG_UID' => $range[1],
1816 while ($item =
$result->fetch())
1818 $item[
'MAILBOX_USER_ID'] = $this->mailbox[
'USER_ID'];
1819 $excerpt[$item[
'ID']] = $item;
1831 $messageUid = md5(sprintf(
'%s:%u:%u', $dirPath, $uidtoken, $item[
'UID']));
1833 if (array_key_exists($messageUid, $excerpt))
1835 $excerptSeen = $excerpt[$messageUid][
'IS_SEEN'];
1836 $excerptSeenYN = in_array($excerptSeen,
array(
'Y',
'S')) ?
'Y' :
'N';
1837 $messageSeen = preg_grep(
'/^ \x5c Seen $/ix', $item[
'FLAGS']) ?
'Y' :
'N';
1839 if ($messageSeen != $excerptSeen)
1841 if (in_array($excerptSeen,
array(
'S',
'U')))
1843 $excerpt[$messageUid][
'IS_SEEN'] = $excerptSeenYN;
1844 $update[$excerptSeenYN][$messageUid] = $excerpt[$messageUid];
1846 if ($messageSeen != $excerptSeenYN)
1848 $update[$excerptSeen][] = $item[
'UID'];
1853 $excerpt[$messageUid][
'IS_SEEN'] = $messageSeen;
1854 $update[$messageSeen][$messageUid] = $excerpt[$messageUid];
1858 unset($excerpt[$messageUid]);
1864 if (!isset($cache[$this->mailbox[
'ID']][$dirPath][$uidtoken]))
1866 $cache[$this->mailbox[
'ID']][$dirPath][$uidtoken] = [
1872 $firstLocalUID = $cache[$this->mailbox[
'ID']][$dirPath][$uidtoken][
'firstLocalUID'];
1873 $lastLocalUID = $cache[$this->mailbox[
'ID']][$dirPath][$uidtoken][
'lastLocalUID'];
1876 $firstLocalUID > 0 &&
1877 $lastLocalUID > 0 &&
1878 (
int) $item[
'UID'] > $firstLocalUID &&
1879 (
int) $item[
'UID'] < $lastLocalUID
1882 $lostMessageFields = [
1883 'ID' => $messageUid,
1884 'DIR_MD5' => md5($dirPath),
1885 'DIR_UIDV' => $uidtoken,
1886 'MSG_UID' => $item[
'UID'],
1887 'MAILBOX_ID' => $this->mailbox[
'ID'],
1896 $countDeleted =
count($excerpt);
1898 foreach ($update as $seen =>
$items)
1902 if (in_array($seen,
array(
'S',
'U')))
1904 $method =
'S' == $seen ?
'seen' :
'unseen';
1905 $this->client->$method(
$items, $dirPath);
1915 while ($offset < $totalValues)
1917 $batchValues = array_slice(
$items, $offset, $batchSize,
true);
1921 '@ID' => array_keys($batchValues),
1929 $offset += $batchSize;
1935 if (!empty($excerpt))
1937 $totalValues =
count($excerpt);
1941 while ($offset < $totalValues)
1943 $batchValues = array_slice($excerpt, $offset, $batchSize,
true);
1947 '@ID' => array_keys($batchValues),
1948 '=DIR_MD5' => md5($dirPath),
1951 'info' =>
'deletion of non-existent messages',
1956 $offset += $batchSize;
1960 $this->lastSyncResult[
'updatedMessages'] += $countUpdated;
1961 $this->lastSyncResult[
'deletedMessages'] += $countDeleted;
1966 $result = Mail\MailMessageUidTable::update(
1969 'MAILBOX_ID' => $this->mailbox[
'ID'],
1979 protected function syncMessage($dirPath,
array $message, &$hashesMap = [], $ignoreSyncFrom =
false, $isOutgoing =
false, $isRecovered =
false)
1983 if (
$fields[
'MESSAGE_ID'] > 0)
1989 if (array_key_exists(
$fields[
'HEADER_MD5'], $hashesMap) && $hashesMap[
$fields[
'HEADER_MD5']] > 0)
1995 $replaces =
$message[
'__replaces'] ??
null;
1997 if ($isOutgoing || !is_null($replaces))
2011 $isOutgoing = ($this->
getDirsHelper()->getOutcomePath() === $dirPath);
2013 $idFromHeaderMessage =
'';
2017 $idFromHeaderMessage = trim(
$message[
'__header']->GetHeader(
"MESSAGE-ID"),
" <>");
2020 if (!$this->
registerMessage(
$fields, isOutgoing: $isOutgoing, idFromHeaderMessage: $idFromHeaderMessage))
2028 if($minimumSyncDate !==
false && !$ignoreSyncFrom &&
$message[
'__internaldate']->getTimestamp() < $this->
getMinimumSyncDate())
2034 if (!empty(
$message[
'__created']) && !empty($this->mailbox[
'OPTIONS'][
'resync_from']))
2036 if (
$message[
'__created']->getTimestamp() < $this->mailbox[
'OPTIONS'][
'resync_from'])
2043 if (
$fields[
'MESSAGE_ID'] > 0)
2062 if (!
$message[
'__bodystructure']->isMultipart())
2064 if (is_array(
$message[
'__parts']) && !empty(
$message[
'__parts'][
'BODY[1]']))
2083 'timestamp' =>
$message[
'__internaldate']->getTimestamp(),
2085 'outcome' => in_array($this->mailbox[
'EMAIL'],
$message[
'__from']),
2086 'draft' =>
$dir !=
null &&
$dir->isDraft() || (isset(
$message[
'FLAGS']) && preg_grep(
'/^ \x5c Draft $/ix',
$message[
'FLAGS'])),
2087 'trash' =>
$dir !=
null &&
$dir->isTrash(),
2088 'spam' =>
$dir !=
null &&
$dir->isSpam(),
2089 'seen' =>
$fields[
'IS_SEEN'] ==
'Y',
2090 'recovered' => $isRecovered,
2091 'hash' =>
$fields[
'HEADER_MD5'],
2092 'lazy_attachments' => $this->isSupportLazyAttachments(),
2113 if (empty($excerpt[
'MSG_UID']) || empty($excerpt[
'DIR_MD5']))
2118 $dirPath = $this->
getDirsHelper()->getDirPathByHash($excerpt[
'DIR_MD5']);
2119 if (empty($dirPath))
2124 $message = $this->client->fetch(
true, $dirPath, $excerpt[
'MSG_UID'],
'(BODYSTRUCTURE)',
$error);
2125 if (empty(
$message[
'BODYSTRUCTURE']))
2137 if (!is_array(
$message[
'BODYSTRUCTURE']))
2140 new Main\
Error(
'Helper\Mailbox\Imap: Invalid BODYSTRUCTURE', 0),
2151 self::MESSAGE_PARTS_ATTACHMENT
2154 $attachments =
array();
2156 $message[
'__bodystructure']->traverse(
2159 if ($item->isMultipart() || $item->isBodyText())
2166 $parts[sprintf(
'BODY[%s.MIME]', $item->getNumber())],
2167 $this->mailbox[
'LANG_CHARSET']
2169 $parts[sprintf(
'BODY[%s]', $item->getNumber())],
2170 $this->mailbox[
'LANG_CHARSET']
2175 return $attachments;
2185 if (!is_array(
$message[
'__parts']))
2202 $complete =
function (&$html, &
$text)
2204 if (
'' !== $html &&
'' ===
$text)
2206 $text = html_entity_decode(
2208 ENT_QUOTES | ENT_HTML401,
2209 $this->mailbox[
'LANG_CHARSET']
2214 $html = txtToHtml(
$text,
false, 120);
2218 [$bodyHtml, $bodyText, $attachments] =
$message[
'__bodystructure']->traverse(
2225 $attachments =
array();
2227 if ($item->isMultipart())
2229 if (
'alternative' === $item->getSubtype())
2231 foreach ($subparts as $part)
2235 if (
'' !== $part[0])
2240 if (
'' !== $part[1])
2245 if (!empty($part[2]))
2247 $attachments = array_merge($attachments, $part[2]);
2251 $complete($html,
$text);
2255 foreach ($subparts as $part)
2259 $complete($part[0], $part[1]);
2261 if (
'' !== $part[0] ||
'' !== $part[1])
2263 $html .= $part[0] .
"\r\n\r\n";
2264 $text .= $part[1] .
"\r\n\r\n";
2267 $attachments = array_merge($attachments, $part[2]);
2271 $html = trim($html);
2276 if (array_key_exists(sprintf(
'BODY[%s]', $item->getNumber()), $parts))
2280 $parts[sprintf(
'BODY[%s.MIME]', $item->getNumber())],
2281 $this->mailbox[
'LANG_CHARSET']
2283 $parts[sprintf(
'BODY[%s]', $item->getNumber())],
2284 $this->mailbox[
'LANG_CHARSET']
2290 'CONTENT-TYPE' => $item->getType() .
'/' . $item->getSubtype(),
2291 'CONTENT-ID' => $item->getId(),
2293 'FILENAME' => $item->getParams()[
'name']
2297 if (!$item->isBodyText())
2299 $attachments[] = $part;
2303 if (
'html' === $item->getSubtype())
2305 $html = $part[
'BODY'];
2309 $text = $part[
'BODY'];
2318 $complete($bodyHtml, $bodyText);
2320 return \CMailMessage::saveMessage(
2321 $this->mailbox[
'ID'],
2333 $minimumDate =
false;
2335 if(!empty($this->mailbox[
'OPTIONS'][
'sync_from']))
2337 $minimumDate = $this->mailbox[
'OPTIONS'][
'sync_from'];
2342 if($syncOldLimit > 0)
2344 $syncOldLimit = strtotime(sprintf(
'-%u days', $syncOldLimit));
2349 if($minimumDate ===
false || $minimumDate < $syncOldLimit)
2351 $minimumDate = $syncOldLimit;
2354 return $minimumDate;
2357 protected function getSyncRange($dirPath, &$uidtoken, $intervalSynchronizationAttempts = 0)
2359 $meta = $this->client->select($dirPath,
$error);
2360 if (
false === $meta)
2362 $this->warnings->add($this->client->getErrors()->toArray());
2367 if (!($meta[
'exists'] > 0))
2372 $uidtoken = $meta[
'uidvalidity'];
2380 $rangeGetter =
function ($min,
$max) use ($dirPath, $uidtoken, &$rangeGetter, $maximumLengthSynchronizationInterval)
2383 $size =
$max - $min + 1;
2386 $d = $size < 1000 ? $maximumLengthSynchronizationInterval : pow(10, round(ceil(log10($size) - 0.7) / 2) * 2 - 2);
2397 if (
count($set) > 1 && end($set) >=
$max)
2406 $set = $this->client->fetch(
false, $dirPath, join(
',', $set),
'(UID)',
$error);
2415 static $uidMinInDatabase, $uidMaxInDatabase, $takeFromDown;
2417 if (!isset($uidMinInDatabase, $uidMaxInDatabase, $takeFromDown))
2419 $messagesUidBoundariesIntervalInDatabase = $this->
getUidRange($dirPath, $uidtoken);
2421 if ($messagesUidBoundariesIntervalInDatabase)
2423 $uidMinInDatabase = $messagesUidBoundariesIntervalInDatabase[
'MIN'];
2424 $uidMaxInDatabase = $messagesUidBoundariesIntervalInDatabase[
'MAX'];
2425 $takeFromDown = $messagesUidBoundariesIntervalInDatabase[
'TAKE_FROM_DOWN'];
2429 $takeFromDown =
true;
2430 $uidMinInDatabase = $uidMaxInDatabase = (end($set)[
'UID'] + 1);
2434 if (
count($set) == 1)
2436 $uid = reset($set)[
'UID'];
2438 if (
$uid > $uidMaxInDatabase ||
$uid < $uidMinInDatabase)
2443 elseif (end($set)[
'UID'] > $uidMaxInDatabase)
2452 $max = current($set)[
'id'];
2453 $min = prev($set)[
'id'];
2455 while (current($set)[
'UID'] > $uidMaxInDatabase && prev($set) && next($set));
2457 if (
$max - $min > $maximumLengthSynchronizationInterval)
2459 return $rangeGetter($min,
$max);
2470 max($set[$min][
'UID'], $uidMaxInDatabase + 1),
2475 elseif (reset($set)[
'UID'] < $uidMinInDatabase && $takeFromDown)
2479 $min = current($set)[
'id'];
2480 $max = next($set)[
'id'];
2482 while (current($set)[
'UID'] < $uidMinInDatabase && next($set) && prev($set));
2484 if (
$max - $min > $maximumLengthSynchronizationInterval)
2486 return $rangeGetter($min,
$max);
2497 min($set[
$max][
'UID'], $uidMinInDatabase - 1),
2506 return $rangeGetter(1, $meta[
'exists']);
2512 '=DIR_MD5' => md5(Emoji::encode($dirPath)),
2513 '=DIR_UIDV' => $uidtoken,
2519 $takeFromDown =
true;
2524 'MIN' =>
'MSG_UID',
'INTERNALDATE'
2535 if(isset($min[
'INTERNALDATE']) && $minimumSyncDate !==
false && $min[
'INTERNALDATE']->getTimestamp() < $minimumSyncDate)
2537 $takeFromDown =
false;
2547 'MSG_UID' =>
'DESC',
2557 'MIN' => $min[
'MIN'],
2558 'MAX' =>
$max[
'MAX'],
2559 'TAKE_FROM_DOWN' => $takeFromDown,
if(! $messageFields||!isset($messageFields['message_id'])||!isset($messageFields['status'])||!CModule::IncludeModule("messageservice")) $messageId
static overwriteMessageHeaders(Main\Mail\Mail $message, array $headers)
syncDirForSpecificDay($dirPath, $internalDate)
resyncDir($dirPath, $numberForResync=false)
downloadAttachments(array &$excerpt)
createMessage(Main\Mail\Mail $message, array $fields=array())
getUidRange($dirPath, $uidtoken)
listDirs($pattern, $useDb=false)
getLocalMessageIdFromHeader($message)
downloadMessageParts(array &$excerpt, Mail\Imap\BodyStructure $bodystructure, $flags=Imap::MESSAGE_PARTS_ALL)
getSyncRange($dirPath, &$uidtoken, $intervalSynchronizationAttempts=0)
const MESSAGE_PARTS_ATTACHMENT
const MAXIMUM_SYNCHRONIZATION_LENGTHS_OF_INTERVALS
setIsOldStatusesLowerThan($internalDate, $dirPath, $mailboxId)
syncMessages($mailboxID, $dirPath, $UIDs, $isRecovered=false)
resyncDirInternal($dir, $numberForResync=false)
getMessageInFolderFilter(Mail\Internals\Entity\MailboxDirectory $dir)
buildMessageIdForDataBase($dirPath, $uidToken, $UID)
downloadMessage(array &$excerpt)
getMaximumSynchronizationLengthsOfIntervals($num)
resyncMessages($dirPath, $uidtoken, &$messages)
checkMessagesForExistence($dirPath='INBOX', $UIDs=[])
completeMessageSync($uid)
searchExistingMessagesByHeaderInDataBase($headerHashes)
uploadMessage(Main\Mail\Mail $message, array &$excerpt=null)
cacheMessage(&$message, $params=array())
removeExistingMessagesFromSynchronizationList($dirPath, $uidToken, &$messages)
moveMailsToFolder($messages, $folderTo)
linkWithExistingMessages(&$messages)
buildMessageHeaderHashForDataBase($message)
getFolderToMessagesMap($messages)
syncMessage($dirPath, array $message, &$hashesMap=[], $ignoreSyncFrom=false, $isOutgoing=false, $isRecovered=false)
searchExistingMessagesByIdInDataBase($idsForDataBase)
fillMessageFields(&$message, $dirPath, $uidToken)
static getCurrentSyncDir()
static setCurrentSyncDir(string $path)
registerMessage(&$fields, $replaces=null, $isOutgoing=false, string $idFromHeaderMessage='', $redefineInsertDate=true, string $messageStatus=\Bitrix\Mail\MailMessageUidTable::DOWNLOADED)
updateMessagesRegistry(array $filter, array $fields, $mailData=array())
listMessages($params=array(), $fetch=true)
setLastSyncResult(array $data)
isSupportLazyAttachments()
unregisterMessages($filter, $eventData=[], $ignoreDeletionCheck=false)
static getStartInternalDateForDir( $mailboxId, ?string $dirPath=null, ?string $dirMd5=null,)
const FIELD_SANITIZE_ON_VIEW
static getFirstLocalUID(int $mailboxId, string $dirPath, string $dirUIDv)
static getLastLocalUID(int $mailboxId, string $dirPath, string $dirUIDv)
static updateSyncTime($id, $val)
static update($primary, array $data)
static createFromPhp(\DateTime $datetime)
static decodeMessageBody($header, $body, $charset)
static parseHeader($header, $charset)
static extractAllMailAddresses($emails)
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
if(!Loader::includeModule('sale')) $pattern