1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
mailbox.php
См. документацию.
1<?php
2
3namespace Bitrix\Mail\Helper;
4
5use Bitrix\Mail;
6use Bitrix\Mail\Internals\MessageUploadQueueTable;
7use Bitrix\Mail\MailboxTable;
8use Bitrix\Mail\MailMessageUidTable;
9use Bitrix\Mail\MailServicesTable;
10use Bitrix\Main;
11use Bitrix\Main\Config\Option;
12use Bitrix\Main\Loader;
13use Bitrix\Main\ORM;
14use Bitrix\Main\ORM\Query\Query;
15use Bitrix\Mail\Helper;
16use Bitrix\Mail\MailMessageTable;
17use Bitrix\Main\ORM\Query\Result;
18
19abstract class Mailbox
20{
21 const SYNC_TIMEOUT = 300;
22 const SYNC_TIME_QUOTA = 280;
29
31 'yandex',
32 'mail.ru',
33 ];
34
36 protected $mailbox;
37 protected $dirsHelper;
38 protected $filters;
39 protected $session;
41 protected $syncParams = [];
42 protected $errors, $warnings;
43 protected $lastSyncResult = [
44 'newMessages' => 0,
45 'newMessagesNotify' => 0,
46 'deletedMessages' => 0,
47 'updatedMessages' => 0,
48 'newMessageId' => null,
49 ];
50
51 public static function isRuZone(): bool
52 {
53 return Loader::includeModule('bitrix24')
54 ? in_array(\CBitrix24::getPortalZone(), ['ru', 'kz', 'by'])
55 : in_array(LANGUAGE_ID, ['ru', 'kz', 'by']);
56 }
57
58 public static function getServices(): array
59 {
60 $res = MailServicesTable::getList([
61 'filter' => [
62 '=ACTIVE' => 'Y',
63 '=SITE_ID' => SITE_ID,
64 ],
65 'order' => [
66 'SORT' => 'ASC',
67 'NAME' => 'ASC',
68 ],
69 ]);
70
71 $services = [];
72
73 while ($service = $res->fetch())
74 {
75 if(!self::isRuZone() && in_array($service['NAME'], self::MAIL_SERVICES_ONLY_FOR_THE_RU_ZONE, true))
76 {
77 continue;
78 }
79
80 $serviceFinal = [
81 'id' => $service['ID'],
82 'type' => $service['SERVICE_TYPE'],
83 'name' => $service['NAME'],
84 'link' => $service['LINK'],
85 'icon' => MailServicesTable::getIconSrc($service['NAME']),
86 'server' => $service['SERVER'],
87 'port' => $service['PORT'],
88 'encryption' => $service['ENCRYPTION'],
89 'token' => $service['TOKEN'],
90 'flags' => $service['FLAGS'],
91 'sort' => $service['SORT'],
92 ];
93
94 $services[] = $serviceFinal;
95 }
96
97 return $services;
98 }
99
108 public static function createInstance($id, $throw = true): Mailbox|bool
109 {
110 return static::rawInstance(array('=ID' => (int) $id, '=ACTIVE' => 'Y'), $throw);
111 }
112
113 public function getDirsMd5WithCounter($mailboxId)
114 {
115 if($this->dirsMd5WithCounter)
116 {
117 return $this->dirsMd5WithCounter;
118 }
119
120 $countersById = [];
122 'select' => [
123 'DIRECTORY_ID' => 'ENTITY_ID',
124 'UNSEEN' => 'VALUE',
125 ],
126 'filter' => [
127 '=ENTITY_TYPE' => 'DIR',
128 '=MAILBOX_ID' => $mailboxId,
129 ],
130 ]);
131 while ($item = $counterResult->fetch()) {
132 $countersById[(int)$item['DIRECTORY_ID']] = (int)$item['UNSEEN'];
133 }
134
135 if (empty($countersById)) {
136 return [];
137 }
138
139 $directoriesWithCounter = [];
141 ->whereIn('ID', array_keys($countersById))
142 ->setSelect([
143 'ID',
144 'DIR_MD5',
145 ])
146 ->where('MAILBOX_ID', $mailboxId)
147 ->exec();
148 while ($item = $res->fetch()) {
149 $id = $item['ID'];
150 $dirMd5 = $item['DIR_MD5'];
151 $directoriesWithCounter[$dirMd5] = [
152 'UNSEEN' => $countersById[$id] ?? 0,
153 'DIR_MD5' => $dirMd5,
154 ];
155 }
156
157 $this->dirsMd5WithCounter = $directoriesWithCounter;
158
159 return $directoriesWithCounter;
160 }
161
162 public function sendCountersEvent()
163 {
164 \CPullWatch::addToStack(
165 'mail_mailbox_' . $this->mailbox['ID'],
166 [
167 'params' => [
168 'mailboxId' => $this->mailbox['ID'],
169 'dirs' => $this->getDirsWithUnseenMailCounters(),
170 ],
171 'module_id' => 'mail',
172 'command' => 'counters_is_synchronized',
173 ]
174 );
176 }
177
179 {
180 global $USER;
181 $mailboxId = $this->mailbox['ID'];
182
183 if (!Helper\Message::isMailboxOwner($mailboxId, $USER->GetID()))
184 {
185 return false;
186 }
187
188 $syncDirs = $this->getDirsHelper()->getSyncDirs();
189 $defaultDirPath = $this->getDirsHelper()->getDefaultDirPath();
190 $dirs = [];
191
192 $dirsMd5WithCountOfUnseenMails = $this->getDirsMd5WithCounter($mailboxId);
193
194 $defaultDirPathId = null;
195
196 foreach ($syncDirs as $dir)
197 {
198 $newDir = [];
199 $newDir['path'] = $dir->getPath(true);
200 $newDir['name'] = $dir->getName();
201 $newDir['count'] = 0;
202 $currentDirMd5WithCountsOfUnseenMails = $dirsMd5WithCountOfUnseenMails[$dir->getDirMd5()];
203
204 if ($currentDirMd5WithCountsOfUnseenMails !== null)
205 {
206 $newDir['count'] = $currentDirMd5WithCountsOfUnseenMails['UNSEEN'];
207 }
208
209 if($newDir['path'] === $defaultDirPath)
210 {
211 $defaultDirPathId = count($dirs);
212 }
213
214 $dirs[] = $newDir;
215 }
216
217 if (empty($dirs))
218 {
219 $dirs = [
220 [
221 'count' => 0,
222 'path' => $defaultDirPath,
223 'name' => $defaultDirPath,
224 ],
225 ];
226 }
227
228 //inbox always on top
229 array_unshift( $dirs, array_splice($dirs, $defaultDirPathId, 1)[0] );
230
231 return $dirs;
232 }
233
242 public static function rawInstance($filter, $throw = true)
243 {
244 try
245 {
246 $mailbox = static::prepareMailbox($filter);
247
248 return static::instance($mailbox);
249 }
250 catch (\Exception $e)
251 {
252 if ($throw)
253 {
254 throw $e;
255 }
256 else
257 {
258 return false;
259 }
260 }
261 }
262
263 protected static function instance(array $mailbox)
264 {
265 // @TODO: other SERVER_TYPE
266 $types = array(
267 'imap' => 'Bitrix\Mail\Helper\Mailbox\Imap',
268 'controller' => 'Bitrix\Mail\Helper\Mailbox\Imap',
269 'domain' => 'Bitrix\Mail\Helper\Mailbox\Imap',
270 'crdomain' => 'Bitrix\Mail\Helper\Mailbox\Imap',
271 );
272
273 if (empty($mailbox))
274 {
275 throw new Main\ObjectException('no mailbox');
276 }
277
278 if (empty($mailbox['SERVER_TYPE']) || !array_key_exists($mailbox['SERVER_TYPE'], $types))
279 {
280 throw new Main\ObjectException('unsupported mailbox type');
281 }
282
283 return new $types[$mailbox['SERVER_TYPE']]($mailbox);
284 }
285
286 public static function prepareMailbox($filter)
287 {
288 if (is_scalar($filter))
289 {
290 $filter = array('=ID' => (int) $filter);
291 }
292
293 static $cachedMailboxes = [];
294
295 $cacheKey = null;
296
297 //For additional security purposes
298 if (isset($filter['=ID']))
299 {
300 $cacheKey = md5(serialize($filter)).'-'.$filter['=ID'];
301 }
302
303 if (is_null($cacheKey) || !isset($cachedMailboxes[$cacheKey]))
304 {
305 $mailbox = Mail\MailboxTable::getList([
306 'filter' => $filter,
307 'select' => [
308 '*',
309 'LANG_CHARSET' => 'SITE.CULTURE.CHARSET'
310 ],
311 'limit' => 1,
312 ])->fetch() ?: [];
313
314 if (!is_null($cacheKey))
315 {
316 $cachedMailboxes[$cacheKey] = $mailbox;
317 }
318 }
319 else
320 {
321 $mailbox = $cachedMailboxes[$cacheKey];
322 }
323
324 if (!empty($mailbox))
325 {
326 if (in_array($mailbox['SERVER_TYPE'], array('controller', 'crdomain', 'domain')))
327 {
328 $result = \CMailDomain2::getImapData(); // @TODO: request controller for 'controller' and 'crdomain'
329
330 $mailbox['SERVER'] = $result['server'];
331 $mailbox['PORT'] = $result['port'];
332 $mailbox['USE_TLS'] = $result['secure'];
333 }
334
336 }
337
338 return $mailbox;
339 }
340
341 public function setSyncParams(array $params = array())
342 {
343 $this->syncParams = $params;
344 }
345
346 protected function __construct($mailbox)
347 {
348 $this->startTime = time();
349 if (defined('START_EXEC_PROLOG_BEFORE_1'))
350 {
351 $startTime = 0;
352 if (is_float(START_EXEC_PROLOG_BEFORE_1))
353 {
354 $startTime = START_EXEC_PROLOG_BEFORE_1;
355 }
356 elseif (preg_match('/ (\d+)$/', START_EXEC_PROLOG_BEFORE_1, $matches))
357 {
358 $startTime = $matches[1];
359 }
360
361 if ($startTime > 0 && $this->startTime > $startTime)
362 {
363 $this->startTime = $startTime;
364 }
365 }
366
367 $this->syncTimeout = static::getTimeout();
368
369 $this->mailbox = $mailbox;
370
372
373 $this->setCheckpoint();
374
375 $this->session = md5(uniqid(''));
376 $this->errors = new Main\ErrorCollection();
377 $this->warnings = new Main\ErrorCollection();
378 }
379
380 protected function normalizeMailboxOptions()
381 {
382 if (empty($this->mailbox['OPTIONS']) || !is_array($this->mailbox['OPTIONS']))
383 {
384 $this->mailbox['OPTIONS'] = array();
385 }
386 }
387
388 public function getMailbox()
389 {
390 return $this->mailbox;
391 }
392
393 public function getMailboxId(): int
394 {
395 $mailbox = self::getMailbox();
396
397 if (isset($mailbox['ID']))
398 {
399 return (int) $mailbox['ID'];
400 }
401
402 return 0;
403 }
404
405 public function getMailboxOwnerId(): int
406 {
407 $mailbox = self::getMailbox();
408
409 if (isset($mailbox['USER_ID']))
410 {
411 return (int) $mailbox['USER_ID'];
412 }
413
414 return 0;
415 }
416
417 /*
418 Additional check that the quota has not been exceeded
419 since the actual creation of the mailbox instance in php
420 */
421 protected function isTimeQuotaExceeded()
422 {
423 return time() - $this->startTime > ceil(static::getTimeout() * 0.9);
424 }
425
426 public function setCheckpoint()
427 {
428 $this->checkpoint = time();
429 }
430
432 {
433 \CUserCounter::set(
434 $userId,
435 'mail_unseen',
436 Message::getCountersForUserMailboxes($userId, true),
437 $this->mailbox['LID']
438 );
439 }
440
442 {
443 $this->updateGlobalCounter($this->mailbox['USER_ID']);
444 }
445
446 private function findMessagesWithAnEmptyBody(int $count, $mailboxId)
447 {
448 $reSyncTime = (new Main\Type\DateTime())->add('- '.static::MESSAGE_RESYNCHRONIZATION_TIME.' seconds');
449
451 [
452 'select' => ['ENTITY_ID'],
453 'filter' =>
454 [
455 '=MAILBOX_ID' => $mailboxId,
456 '=ENTITY_TYPE' => 'MESSAGE',
457 '=PROPERTY_NAME' => 'UNSYNC_BODY',
458 '=VALUE' => 'Y',
459 '<=DATE_INSERT' => $reSyncTime,
460 ]
461 ,
462 'limit' => $count,
463 ]
464 )->fetchAll();
465
466 return array_map(
467 function ($item)
468 {
469 return $item['ENTITY_ID'];
470 },
471 $ids
472 );
473 }
474
475 private function getLostMessages(int $count, array $additionalFilters = []): Main\ORM\Query\Result
476 {
477 return MailMessageUidTable::getList([
478 'select' => array(
479 'MSG_UID',
480 'DIR_MD5',
481 ),
482 'filter' => array_merge([
483 '=MAILBOX_ID' => $this->mailbox['ID'],
485 ], $additionalFilters),
486 'limit' => $count,
487 ]);
488 }
489
490 private function removeOldUnderloadedMessages(int $limit, array $additionalFilters = []): bool
491 {
492 $resyncTime = new Main\Type\DateTime();
493 $resyncTime->add('- '.static::INCOMPLETE_MESSAGE_REMOVE_TIMEOUT.' seconds');
494
496 array_merge([
497 '=MAILBOX_ID' => $this->mailbox['ID'],
498 '=MESSAGE_ID' => '0',
499 '=IS_OLD' => \Bitrix\Mail\MailMessageUidTable::DOWNLOADED,
500 '<=DATE_INSERT' => $resyncTime,
501 ], $additionalFilters),
502 limit: $limit,
503 sendEvent: false
504 );
505 }
506
507 private function syncIncompleteMessages(Main\ORM\Query\Result $messages): void
508 {
509 $mailboxId = $this->mailbox['ID'];
510
511 while ($item = $messages->fetch())
512 {
513 $dirPath = $this->getDirsHelper()->getDirPathByHash($item['DIR_MD5']);
514 $this->syncMessages($mailboxId, $dirPath, [$item['MSG_UID']], true);
515
516 if(Main\Loader::includeModule('pull'))
517 {
518 \CPullWatch::addToStack(
519 'mail_mailbox_' . $mailboxId,
520 [
521 'params' => [
522 'dir' => $dirPath,
523 'mailboxId' => $mailboxId,
524 ],
525 'module_id' => 'mail',
526 'command' => 'recovered_message_is_synchronized',
527 ]
528 );
529 \Bitrix\Pull\Event::send();
530 }
531 }
532 }
533
534 public function reSyncStartPage(): void
535 {
536 $this->resyncDir($this->getDirsHelper()->getDefaultDirPath(),25);
537 }
538
539 public function restoringConsistency(): void
540 {
541 $dirsSync = $this->getDirsHelper()->getSyncDirsOrderByTime();
542
543 foreach ($dirsSync as $dir)
544 {
545 $messageInFolderFilter = $this->getMessageInFolderFilter($dir);
546 $this->removeOldUnderloadedMessages(static::NUMBER_OF_INCOMPLETE_MESSAGES_TO_REMOVE, $messageInFolderFilter);
547 $this->syncIncompleteMessages($this->getLostMessages(static::NUMBER_OF_BROKEN_MESSAGES_TO_RESYNCHRONIZE, $messageInFolderFilter));
548 }
549
550 \Bitrix\Mail\Helper\Message::reSyncBody($this->mailbox['ID'], $this->findMessagesWithAnEmptyBody(static::NUMBER_OF_BROKEN_MESSAGES_TO_RESYNCHRONIZE, $this->mailbox['ID']));
551 }
552
553 public function syncCounters(): void
554 {
555 Helper::setMailboxUnseenCounter($this->mailbox['ID'],Helper::updateMailCounters($this->mailbox));
556
557 $usersWithAccessToMailbox = Mailbox\SharedMailboxesManager::getUserIdsWithAccessToMailbox($this->mailbox['ID']);
558
559 foreach ($usersWithAccessToMailbox as $userId)
560 {
562 }
563 }
564
565 public function sync($syncCounters = true)
566 {
567 global $DB;
568
569 /*
570 Setting a new time for an attempt to synchronize the mailbox
571 through the agent for users with a free tariff
572 */
573 if (!LicenseManager::isSyncAvailable() || !LicenseManager::checkTheMailboxForSyncAvailability((int)$this->mailbox['ID'], (int)$this->mailbox['USER_ID']))
574 {
575 $this->mailbox['OPTIONS']['next_sync'] = time() + 3600 * 24;
576
577 return 0;
578 }
579
580 /*
581 Do not start synchronization if no more than static::getTimeout() have passed since the previous one
582 */
583 if (time() - $this->mailbox['SYNC_LOCK'] < static::getTimeout())
584 {
585 return 0;
586 }
587
588 $this->mailbox['SYNC_LOCK'] = time();
589
590 /*
591 Additional check that the quota has not been exceeded
592 since the actual creation of the mailbox instance in php
593 */
594 if ($this->isTimeQuotaExceeded())
595 {
596 return 0;
597 }
598
599 $this->session = md5(uniqid(''));
600
601 $this->syncOutgoing();
602 $this->restoringConsistency();
603 $this->reSyncStartPage();
604
605 $lockSql = sprintf(
606 'UPDATE b_mail_mailbox SET SYNC_LOCK = %u WHERE ID = %u AND (SYNC_LOCK IS NULL OR SYNC_LOCK < %u)',
607 $this->mailbox['SYNC_LOCK'], $this->mailbox['ID'], $this->mailbox['SYNC_LOCK'] - static::getTimeout()
608 );
609
610 /*
611 If the time record for blocking synchronization has not been added to the table,
612 we will have to abort synchronization
613 */
614 if (!$DB->query($lockSql)->affectedRowsCount())
615 {
616 return 0;
617 }
618
619 $mailboxSyncManager = new Mailbox\MailboxSyncManager($this->mailbox['USER_ID']);
620 if ($this->mailbox['USER_ID'] > 0)
621 {
622 $mailboxSyncManager->setSyncStartedData($this->mailbox['ID']);
623 }
624
625 $syncReport = $this->syncInternal();
626 $count = $syncReport['syncCount'];
627
628 if($syncReport['reSyncStatus'])
629 {
630 /*
631 When folders are successfully resynchronized,
632 allow messages that were left to be moved to be deleted
633 */
635 [
636 '=MAILBOX_ID' => $this->mailbox['ID'],
637 '=MSG_UID' => 0,
638 '=IS_OLD' => 'M',
639 ],
640 [
641 'IS_OLD' => 'R',
642 ],
643 );
644 }
645
646 $success = $count !== false && $this->errors->isEmpty();
647
648 $syncUnlock = $this->isTimeQuotaExceeded() ? 0 : -1;
649
650 $interval = max(1, (int) $this->mailbox['PERIOD_CHECK']) * 60;
651 $syncErrors = max(0, (int) $this->mailbox['OPTIONS']['sync_errors']);
652
653 if ($count === false)
654 {
655 $syncErrors++;
656
657 $maxInterval = 3600 * 24 * 7;
658 for ($i = 1; $i < $syncErrors && $interval < $maxInterval; $i++)
659 {
660 $interval = min($interval * ($i + 1), $maxInterval);
661 }
662 }
663 else
664 {
665 $syncErrors = 0;
666
667 $interval = $syncUnlock < 0 ? $interval : min($count > 0 ? 60 : 600, $interval);
668 }
669
670 $this->mailbox['OPTIONS']['sync_errors'] = $syncErrors;
671 $this->mailbox['OPTIONS']['next_sync'] = time() + $interval;
672
673 $optionsValue = $this->mailbox['OPTIONS'];
674
675 $unlockSql = sprintf(
676 "UPDATE b_mail_mailbox SET SYNC_LOCK = %d, OPTIONS = '%s' WHERE ID = %u AND SYNC_LOCK = %u",
677 $syncUnlock,
678 $DB->forSql(serialize($optionsValue)),
679 $this->mailbox['ID'],
680 $this->mailbox['SYNC_LOCK']
681 );
682 if ($DB->query($unlockSql)->affectedRowsCount())
683 {
684 $this->mailbox['SYNC_LOCK'] = $syncUnlock;
685 }
686
688
689 $this->pushSyncStatus(
690 array(
691 'new' => $count,
692 'updated' => $lastSyncResult['updatedMessages'],
693 'deleted' => $lastSyncResult['deletedMessages'],
694 'complete' => $this->mailbox['SYNC_LOCK'] < 0,
695 ),
696 true
697 );
698
699 $this->notifyNewMessages();
700
701 if ($this->mailbox['USER_ID'] > 0)
702 {
703 $mailboxSyncManager->setSyncStatus($this->mailbox['ID'], $success, time());
704 }
705
706 if($syncCounters)
707 {
708 $this->syncCounters();
709 }
710
711 return $count;
712 }
713
714 public function getSyncStatus()
715 {
716 return -1;
717 }
718
719 protected function pushSyncStatus($params, $force = false)
720 {
721 if (Loader::includeModule('pull'))
722 {
723 $status = $this->getSyncStatus();
724
725 \CPullWatch::addToStack(
726 'mail_mailbox_' . $this->mailbox['ID'],
727 array(
728 'module_id' => 'mail',
729 'command' => 'mailbox_sync_status',
730 'params' => array_merge(
731 array(
732 'id' => $this->mailbox['ID'],
733 'status' => sprintf('%.3f', $status),
734 'sessid' => $this->syncParams['sessid'] ?? $this->session,
735 'timestamp' => microtime(true),
736 ),
737 $params
738 ),
739 )
740 );
741
742 if ($force)
743 {
745 }
746 }
747 }
748
749 public function dismissOldMessages()
750 {
751 global $DB;
752
754 {
755 return true;
756 }
757
758 $startTime = time();
759
760 if (time() - $this->mailbox['SYNC_LOCK'] < static::getTimeout())
761 {
762 return false;
763 }
764
765 if ($this->isTimeQuotaExceeded())
766 {
767 return false;
768 }
769
770 $syncUnlock = $this->mailbox['SYNC_LOCK'];
771
772 $lockSql = sprintf(
773 'UPDATE b_mail_mailbox SET SYNC_LOCK = %u WHERE ID = %u AND (SYNC_LOCK IS NULL OR SYNC_LOCK < %u)',
774 $startTime, $this->mailbox['ID'], $startTime - static::getTimeout()
775 );
776 if ($DB->query($lockSql)->affectedRowsCount())
777 {
778 $this->mailbox['SYNC_LOCK'] = $startTime;
779 }
780 else
781 {
782 return false;
783 }
784
785 $result = true;
786
787 $entity = MailMessageUidTable::getEntity();
788 $connection = $entity->getConnection();
789
790 $whereConditionForOldMessages = sprintf(
791 ' (%s)',
792 ORM\Query\Query::buildFilterSql(
793 $entity,
794 array(
795 '=MAILBOX_ID' => $this->mailbox['ID'],
796 '>MESSAGE_ID' => 0,
797 '<INTERNALDATE' => Main\Type\Date::createFromTimestamp(strtotime(sprintf('-%u days', Mail\Helper\LicenseManager::getSyncOldLimit()))),
798 '!=IS_OLD' => 'Y',
799 )
800 )
801 );
802
803 $sqlHelper = $connection->getSqlHelper();
804
805 while (true)
806 {
807 $oldMessages = \Bitrix\Mail\MailMessageUidTable::query()
808 ->setSelect([
809 'ID',
810 'MAILBOX_ID',
811 'MESSAGE_ID',
812 ])
813 ->setFilter([
814 '=MAILBOX_ID' => $this->mailbox['ID'],
815 '>MESSAGE_ID' => 0,
816 '<INTERNALDATE' => \Bitrix\Main\Type\Date::createFromTimestamp(strtotime(sprintf('-%u days', \Bitrix\Mail\Helper\LicenseManager::getSyncOldLimit()))),
817 ])
818 ->whereNotExists(
820 SELECT 1
821 FROM " . \Bitrix\Mail\Internals\MessageAccessTable::getTableName() . "
822 WHERE
823 MAILBOX_ID = " . \Bitrix\Mail\MailMessageUidTable::query()->getInitAlias() . ".MAILBOX_ID
824 AND MESSAGE_ID = " . \Bitrix\Mail\MailMessageUidTable::query()->getInitAlias() . ".MESSAGE_ID
825 AND ENTITY_TYPE IN ('" . \Bitrix\Mail\Internals\MessageAccessTable::ENTITY_TYPE_TASKS_TASK . "','" . \Bitrix\Mail\Internals\MessageAccessTable::ENTITY_TYPE_BLOG_POST . "')"
826 )
827 )->setLimit(static::MESSAGE_DELETION_LIMIT_AT_A_TIME)->exec()
828 ;
829
830 $messageAsStringForSql = [];
831 $oldMessageIds = [];
832
833 while ($oldMessage = $oldMessages->fetch())
834 {
835 $id = $oldMessage['ID'];
836 $oldMessageIds[] = $id;
837
838 [, $insert] = $sqlHelper->prepareInsert(\Bitrix\Mail\Internals\MessageDeleteQueueTable::getTableName(),
839 [
840 'ID' => $id,
841 'MAILBOX_ID' => (int)$oldMessage['MAILBOX_ID'],
842 'MESSAGE_ID' => (int)$oldMessage['MESSAGE_ID'],
843 ]
844 );
845
846 $messageAsStringForSql[] = "($insert)";
847 }
848
849 if (empty($oldMessageIds))
850 {
851 break;
852 }
853
855 [
856 '!=MESSAGE_ID' => 0,
857 '=MAILBOX_ID' => $this->mailbox['ID'],
858 '@ID' => $oldMessageIds,
859 ],
860 [
861 'MESSAGE_ID' => 0,
862 ],
863 sendEvent: false,
864 );
865
866 $connection->queryExecute(
867 $sqlHelper->getInsertIgnore(
869 '(ID, MAILBOX_ID, MESSAGE_ID)',
870 'VALUES ' . implode(', ', $messageAsStringForSql)
871 )
872 );
873
874 if ($this->isTimeQuotaExceeded() || time() - $this->checkpoint > 15)
875 {
876 $result = false;
877
878 break;
879 }
880 }
881
882 if ($result !== false)
883 {
884 do
885 {
886 $connection->query(sprintf(
887 "UPDATE %s SET IS_OLD = 'Y', IS_SEEN = 'Y' WHERE %s ORDER BY ID LIMIT " . static::MESSAGE_SET_OLD_STATUS_LIMIT_AT_A_TIME,
888 $connection->getSqlHelper()->quote($entity->getDbTableName()),
889 $whereConditionForOldMessages
890 ));
891
892 if ($this->isTimeQuotaExceeded() || time() - $this->checkpoint > 15)
893 {
894 $result = false;
895
896 break;
897 }
898
899 }
900 while ($connection->getAffectedRowsCount() >= static::MESSAGE_SET_OLD_STATUS_LIMIT_AT_A_TIME);
901 }
902
903 $unlockSql = sprintf(
904 "UPDATE b_mail_mailbox SET SYNC_LOCK = %d WHERE ID = %u AND SYNC_LOCK = %u",
905 $syncUnlock, $this->mailbox['ID'], $this->mailbox['SYNC_LOCK']
906 );
907 if ($DB->query($unlockSql)->affectedRowsCount())
908 {
909 $this->mailbox['SYNC_LOCK'] = $syncUnlock;
910 }
911
912 return $result;
913 }
914
916 {
917 global $DB;
918
919 $startTime = time();
920
921 if (time() - $this->mailbox['SYNC_LOCK'] < static::getTimeout())
922 {
923 return false;
924 }
925
926 if ($this->isTimeQuotaExceeded())
927 {
928 return false;
929 }
930
931 $syncUnlock = $this->mailbox['SYNC_LOCK'];
932
933 $lockSql = sprintf(
934 'UPDATE b_mail_mailbox SET SYNC_LOCK = %u WHERE ID = %u AND (SYNC_LOCK IS NULL OR SYNC_LOCK < %u)',
935 $startTime, $this->mailbox['ID'], $startTime - static::getTimeout()
936 );
937 if ($DB->query($lockSql)->affectedRowsCount())
938 {
939 $this->mailbox['SYNC_LOCK'] = $startTime;
940 }
941 else
942 {
943 return false;
944 }
945
946 $minSyncTime = Mail\MailboxDirectory::getMinSyncTime($this->mailbox['ID']);
947
949 [
950 '=MAILBOX_ID' => $this->mailbox['ID'],
951 '!=MESSAGE_ID' => 0,
952 '>DELETE_TIME' => 0,
953 /*The values in the tables are still used to delete related items (example: attachments):*/
954 '<DELETE_TIME' => $minSyncTime,
955 ],
956 [],
957 static::MESSAGE_DELETION_LIMIT_AT_A_TIME
958 );
959
960 $unlockSql = sprintf(
961 "UPDATE b_mail_mailbox SET SYNC_LOCK = %d WHERE ID = %u AND SYNC_LOCK = %u",
962 $syncUnlock, $this->mailbox['ID'], $this->mailbox['SYNC_LOCK']
963 );
964 if ($DB->query($unlockSql)->affectedRowsCount())
965 {
966 $this->mailbox['SYNC_LOCK'] = $syncUnlock;
967 }
968
969 return true;
970 }
971
972 public function cleanup()
973 {
974 do
975 {
977 'runtime' => array(
979 'MESSAGE_UID',
980 'Bitrix\Mail\MailMessageUidTable',
981 array(
982 '=this.MAILBOX_ID' => 'ref.MAILBOX_ID',
983 '=this.MESSAGE_ID' => 'ref.MESSAGE_ID',
984 )
985 ),
986 ),
987 'select' => array('MESSAGE_ID', 'UID' => 'MESSAGE_UID.ID'),
988 'filter' => array(
989 '=MAILBOX_ID' => $this->mailbox['ID'],
990 ),
991 'limit' => 100,
992 ));
993
994 $count = 0;
995 while ($item = $res->fetch())
996 {
997 $count++;
998
999 if (empty($item['UID']))
1000 {
1001 \CMailMessage::delete($item['MESSAGE_ID']);
1002 }
1003
1005 '=MAILBOX_ID' => $this->mailbox['ID'],
1006 '=MESSAGE_ID' => $item['MESSAGE_ID'],
1007 ));
1008
1009 if ($this->isTimeQuotaExceeded() || time() - $this->checkpoint > 60)
1010 {
1011 return false;
1012 }
1013 }
1014 }
1015 while ($count > 0);
1016
1017 return true;
1018 }
1019
1020 protected function listMessages($params = array(), $fetch = true)
1021 {
1022 $filter = array(
1023 '=MAILBOX_ID' => $this->mailbox['ID'],
1024 );
1025
1026 if (!empty($params['filter']))
1027 {
1028 $filter = array_merge((array) $params['filter'], $filter);
1029 }
1030
1031 $params['filter'] = $filter;
1032
1033 $result = MailMessageUidTable::getList($params);
1034
1035 return $fetch ? $result->fetchAll() : $result;
1036 }
1037
1038 protected function findMessageInUploadQueue(
1039 $idFromHeaderMessage,
1040 ): Result
1041 {
1042 return MessageUploadQueueTable::getList([
1043 'select' => [
1044 'ID',
1045 'MESSAGE_ID' => 'UID_TABLE.MESSAGE_ID',
1046 ],
1047 'filter'=> [
1048 '=SYNC_STAGE' => -1,
1049 '=SYNC_LOCK' => 0,
1050 '=MAILBOX_ID'=> $this->mailbox['ID'],
1051 '=UID_TABLE.IS_OLD' => MailMessageUidTable::DOWNLOADED,
1052 '=UID_TABLE.DELETE_TIME' => 0,
1053 '=UID_TABLE.MESSAGE_TABLE.MSG_ID' => $idFromHeaderMessage,
1054 ],
1055 'limit' => 1,
1056 ]);
1057 }
1058
1059 protected function registerMessage(&$fields, $replaces = null, $isOutgoing = false, string $idFromHeaderMessage = '', $redefineInsertDate = true, string $messageStatus = \Bitrix\Mail\MailMessageUidTable::DOWNLOADED): bool
1060 {
1061 $now = new Main\Type\DateTime();
1062
1063 $replacingMessageFromQueue = false;
1064
1065 if (!empty($replaces))
1066 {
1067 /*
1068 To replace the temporary id of outgoing emails with a permanent one
1069 after receiving the uid from the original mail service.
1070 */
1071 if($isOutgoing)
1072 {
1073 if (!is_array($replaces))
1074 {
1075 $replaces = [
1076 '=ID' => $replaces,
1077 ];
1078 }
1079
1080 $exists = MailMessageUidTable::getList([
1081 'select' => [
1082 'ID',
1083 'MESSAGE_ID',
1084 ],
1085 'filter' => [
1086 $replaces,
1087 '=MAILBOX_ID' => $this->mailbox['ID'],
1088 '==DELETE_TIME' => 0,
1089 ],
1090 ])->fetch();
1091 }
1092 else
1093 {
1094 $exists = [
1095 'ID' => $replaces,
1096 'MESSAGE_ID' => $fields['MESSAGE_ID'],
1097 ];
1098 }
1099 }
1100 else if ($isOutgoing && $idFromHeaderMessage !== '')
1101 {
1102 /*
1103 * Find and link an message if the unloading of outgoing emails to the "Sent" folder
1104 * on the service is disabled and the service itself created the email in this folder.
1105 */
1106 $exists = $this->findMessageInUploadQueue(
1107 $idFromHeaderMessage,
1108 )->fetch();
1109
1110 $replacingMessageFromQueue = true;
1111 }
1112
1113 if (!empty($exists))
1114 {
1115 $fields['MESSAGE_ID'] = $exists['MESSAGE_ID'];
1116
1118 array(
1119 '=ID' => $exists['ID'],
1120 '=MAILBOX_ID' => $this->mailbox['ID'],
1121 '==DELETE_TIME' => 0,
1122 ),
1123 array_merge(
1124 $fields,
1125 array(
1126 'TIMESTAMP_X' => $now,
1127 )
1128 ),
1129 array_merge(
1130 $exists,
1131 array(
1132 'MAILBOX_USER_ID' => $this->mailbox['USER_ID'],
1133 )
1134 )
1135 );
1136
1137 if ($replacingMessageFromQueue && $result)
1138 {
1140 'ID' => $exists['ID'],
1141 'MAILBOX_ID' => (int) $this->mailbox['ID'],
1142 ));
1143 }
1144 }
1145 else
1146 {
1147 $checkResult = new ORM\Data\AddResult();
1148 $addFields = array_merge(
1149 [
1150 'MESSAGE_ID' => 0,
1151 ],
1152 $fields,
1153 [
1154 'IS_OLD' => $messageStatus,
1155 'MAILBOX_ID' => $this->mailbox['ID'],
1156 'SESSION_ID' => $this->session,
1157 'TIMESTAMP_X' => $now,
1158 ]
1159 );
1160
1161 if ($redefineInsertDate || !array_key_exists('DATE_INSERT', $fields))
1162 {
1163 $addFields['DATE_INSERT'] = $now;
1164 }
1165
1166 MailMessageUidTable::checkFields($checkResult, null, $addFields);
1167 if (!$checkResult->isSuccess())
1168 {
1169 return false;
1170 }
1171
1172 MailMessageUidTable::mergeData($addFields, [
1173 'MSG_UID' => $addFields['MSG_UID'],
1174 'HEADER_MD5' => $addFields['HEADER_MD5'],
1175 'SESSION_ID' => $addFields['SESSION_ID'],
1176 'TIMESTAMP_X' => $addFields['TIMESTAMP_X'],
1177 ]);
1178
1179 return true;
1180 }
1181
1182 return $result;
1183 }
1184
1185 protected function updateMessagesRegistry(array $filter, array $fields, $mailData = array())
1186 {
1188 array_merge(
1189 $filter,
1190 array(
1191 '!=IS_OLD' => 'Y',
1192 '=MAILBOX_ID' => $this->mailbox['ID'],
1193 )
1194 ),
1195 $fields,
1196 $mailData
1197 );
1198 }
1199
1200 protected function unregisterMessages($filter, $eventData = [], $ignoreDeletionCheck = false)
1201 {
1202 $messageExistInTheOriginalMailbox = false;
1203 $messagesForRemove = [];
1204 $filterForCheck = [];
1205
1206 if(!$ignoreDeletionCheck)
1207 {
1208 $filterForCheck = array_merge(
1209 $filter,
1211 [
1212 '=MAILBOX_ID' => $this->mailbox['ID'],
1213 /*
1214 We check illegally deleted messages,
1215 the disappearance of which the user may notice.
1216 According to such data, it is easier to find a message
1217 in the original mailbox for diagnostics.
1218 */
1219 '!=MESSAGE_ID' => 0,
1220 ]
1221 );
1222
1223 $messagesForRemove = MailMessageUidTable::getList([
1224 'select' => [
1225 'ID',
1226 'MAILBOX_ID',
1227 'DIR_MD5',
1228 'DIR_UIDV',
1229 'MSG_UID',
1230 'INTERNALDATE',
1231 'IS_SEEN',
1232 'DATE_INSERT',
1233 'MESSAGE_ID',
1234 'IS_OLD',
1235 ],
1236 'filter' => $filterForCheck,
1237 'limit' => 100,
1238 ])->fetchAll();
1239
1240 if (!empty($messagesForRemove))
1241 {
1242 if (isset($messagesForRemove[0]['DIR_MD5']))
1243 {
1244 $dirMD5 = $messagesForRemove[0]['DIR_MD5'];
1245 $dirPath = $this->getDirsHelper()->getDirPathByHash($dirMD5);
1246 $UIDs = array_map(
1247 function ($item) {
1248 return $item['MSG_UID'];
1249 },
1250 $messagesForRemove
1251 );
1252
1253 $messageExistInTheOriginalMailbox = $this->checkMessagesForExistence($dirPath, $UIDs);
1254 }
1255 }
1256 }
1257
1258 if($messageExistInTheOriginalMailbox === false)
1259 {
1261 array_merge(
1262 $filter,
1263 [
1264 '=MAILBOX_ID' => $this->mailbox['ID'],
1265 ]
1266 )
1267 );
1268 }
1269 else
1270 {
1271 $messageForLog = isset($messagesForRemove[0]) ? $messagesForRemove[0] : [];
1272
1273 /*
1274 For the log, we take a message from the entire sample,
1275 which was definitely deleted by mistake.
1276 */
1277 foreach($messagesForRemove as $message)
1278 {
1279 if(isset($message['MSG_UID']) && (int)$message['MSG_UID'] === (int)$messageExistInTheOriginalMailbox)
1280 {
1281 $messageForLog = $message;
1282 break;
1283 }
1284 }
1285
1286 if(isset($messageForLog['INTERNALDATE']) && $messageForLog['INTERNALDATE'] instanceof Main\Type\DateTime)
1287 {
1288 $messageForLog['INTERNALDATE'] = $messageForLog['INTERNALDATE']->getTimestamp();
1289 }
1290 if(isset($messageForLog['DATE_INSERT']) && $messageForLog['DATE_INSERT'] instanceof Main\Type\DateTime)
1291 {
1292 $messageForLog['DATE_INSERT'] = $messageForLog['DATE_INSERT']->getTimestamp();
1293 }
1294
1295 if(isset($filterForCheck['@ID']))
1296 {
1297 $filterForCheck['@ID'] = '[hidden for the log]';
1298 }
1299
1307
1308 return false;
1309 }
1310 }
1311
1312 protected function linkMessage($uid, $id)
1313 {
1314 $result = MailMessageUidTable::update(
1315 array(
1316 'ID' => $uid,
1317 'MAILBOX_ID' => $this->mailbox['ID'],
1318 ),
1319 array(
1320 'MESSAGE_ID' => $id,
1321 )
1322 );
1323
1324 return $result->isSuccess();
1325 }
1326
1327 protected function cacheMessage(&$body, $params = array())
1328 {
1329 if (empty($params['origin']) && empty($params['replaces']))
1330 {
1331 $params['lazy_attachments'] = $this->isSupportLazyAttachments();
1332 }
1334
1335 return \CMailMessage::addMessage(
1336 $this->mailbox['ID'],
1337 $body,
1338 $this->mailbox['CHARSET'] ?: $this->mailbox['LANG_CHARSET'],
1339 $params
1340 );
1341 }
1342
1343 public function mail(array $params)
1344 {
1345 class_exists('Bitrix\Mail\Helper');
1346
1348
1349 $messageUid = $this->createMessage($message);
1350
1352 'ID' => $messageUid,
1353 'MAILBOX_ID' => $this->mailbox['ID'],
1354 ));
1355
1356 \CAgent::addAgent(
1357 sprintf(
1358 'Bitrix\Mail\Helper::syncOutgoingAgent(%u);',
1359 $this->mailbox['ID']
1360 ),
1361 'mail', 'N', 60
1362 );
1363 }
1364
1366 {
1367 $messageUid = sprintf('%x%x', time(), rand(0, 0xffffffff));
1368 $body = sprintf(
1369 '%1$s%3$s%3$s%2$s',
1370 $message->getHeaders(),
1371 $message->getBody(),
1372 $message->getMailEol()
1373 );
1374
1375 $messageId = $this->cacheMessage(
1376 $body,
1377 array(
1378 'outcome' => true,
1379 'draft' => false,
1380 'trash' => false,
1381 'spam' => false,
1382 'seen' => true,
1383 'trackable' => true,
1384 'origin' => true,
1385 )
1386 );
1387
1388 $fields = array_merge(
1389 $fields,
1390 array(
1391 'ID' => $messageUid,
1392 'INTERNALDATE' => new Main\Type\DateTime,
1393 'IS_SEEN' => 'Y',
1394 'MESSAGE_ID' => $messageId,
1395 )
1396 );
1397
1398 $this->registerMessage($fields);
1399
1400 return $messageUid;
1401 }
1402
1403 public function syncOutgoing()
1404 {
1405 $res = $this->listMessages(
1406 array(
1407 'runtime' => array(
1408 new \Bitrix\Main\Entity\ReferenceField(
1409 'UPLOAD_QUEUE',
1410 'Bitrix\Mail\Internals\MessageUploadQueueTable',
1411 array(
1412 '=this.ID' => 'ref.ID',
1413 '=this.MAILBOX_ID' => 'ref.MAILBOX_ID',
1414 ),
1415 array(
1416 'join_type' => 'INNER',
1417 )
1418 ),
1419 ),
1420 'select' => array(
1421 '*',
1422 '__' => 'MESSAGE.*',
1423 'UPLOAD_LOCK' => 'UPLOAD_QUEUE.SYNC_LOCK',
1424 'UPLOAD_STAGE' => 'UPLOAD_QUEUE.SYNC_STAGE',
1425 'UPLOAD_ATTEMPTS' => 'UPLOAD_QUEUE.ATTEMPTS',
1426 ),
1427 'filter' => array(
1428 '>=UPLOAD_QUEUE.SYNC_STAGE' => 0,
1429 '<UPLOAD_QUEUE.SYNC_LOCK' => time() - static::getTimeout(),
1430 '<UPLOAD_QUEUE.ATTEMPTS' => 5,
1431 ),
1432 'order' => array(
1433 'UPLOAD_QUEUE.SYNC_LOCK' => 'ASC',
1434 'UPLOAD_QUEUE.SYNC_STAGE' => 'ASC',
1435 'UPLOAD_QUEUE.ATTEMPTS' => 'ASC',
1436 ),
1437 ),
1438 false
1439 );
1440
1441 while ($excerpt = $res->fetch())
1442 {
1443 $n = $excerpt['UPLOAD_ATTEMPTS'] + 1;
1444 $interval = min(static::getTimeout() * pow($n, $n), 3600 * 24 * 7);
1445
1446 if ($excerpt['UPLOAD_LOCK'] > time() - $interval)
1447 {
1448 continue;
1449 }
1450
1451 $this->syncOutgoingMessage($excerpt);
1452
1453 if ($this->isTimeQuotaExceeded())
1454 {
1455 break;
1456 }
1457 }
1458 }
1459
1460 protected function syncOutgoingMessage($excerpt)
1461 {
1462 global $DB;
1463
1464 $lockSql = sprintf(
1465 "UPDATE b_mail_message_upload_queue SET SYNC_LOCK = %u, SYNC_STAGE = %u, ATTEMPTS = ATTEMPTS + 1
1466 WHERE ID = '%s' AND MAILBOX_ID = %u AND SYNC_LOCK < %u",
1467 $syncLock = time(),
1468 max(1, $excerpt['UPLOAD_STAGE']),
1469 $DB->forSql($excerpt['ID']),
1470 $excerpt['MAILBOX_ID'],
1471 $syncLock - static::getTimeout()
1472 );
1473 if (!$DB->query($lockSql)->affectedRowsCount())
1474 {
1475 return;
1476 }
1477
1478 $outgoingBody = $excerpt['__BODY_HTML'];
1479
1481 'select' => array(
1482 'ID', 'FILE_ID', 'FILE_NAME',
1483 ),
1484 'filter' => array(
1485 '=MESSAGE_ID' => $excerpt['__ID'],
1486 ),
1487 ))->fetchAll();
1488
1489 $attachments = array();
1490 if (!empty($excerpt['__files']) && is_array($excerpt['__files']))
1491 {
1492 $hostname = \COption::getOptionString('main', 'server_name', 'localhost');
1493 if (defined('BX24_HOST_NAME') && BX24_HOST_NAME != '')
1494 {
1495 $hostname = BX24_HOST_NAME;
1496 }
1497 else if (defined('SITE_SERVER_NAME') && SITE_SERVER_NAME != '')
1498 {
1499 $hostname = SITE_SERVER_NAME;
1500 }
1501
1502 foreach ($excerpt['__files'] as $item)
1503 {
1504 $file = \CFile::makeFileArray($item['FILE_ID']);
1505
1506 $contentId = sprintf(
1507 'bxacid.%s@%s.mail',
1508 hash('crc32b', $file['external_id'].$file['size'].$file['name']),
1509 hash('crc32b', $hostname)
1510 );
1511
1512 $attachments[] = array(
1513 'ID' => $contentId,
1514 'NAME' => $item['FILE_NAME'],
1515 'PATH' => $file['tmp_name'],
1516 'CONTENT_TYPE' => $file['type'],
1517 );
1518
1519 $outgoingBody = preg_replace(
1520 sprintf('/aid:%u/i', $item['ID']),
1521 sprintf('cid:%s', $contentId),
1522 $outgoingBody
1523 );
1524 }
1525 }
1526
1527 foreach (array('FROM', 'REPLY_TO', 'TO', 'CC', 'BCC') as $field)
1528 {
1529 $field = sprintf('__FIELD_%s', $field);
1530
1531 if (mb_strlen($excerpt[$field]) == 255 && '' != $excerpt['__HEADER'] && empty($parsedHeader))
1532 {
1533 $parsedHeader = \CMailMessage::parseHeader($excerpt['__HEADER'], LANG_CHARSET);
1534
1535 $excerpt['__FIELD_FROM'] = $parsedHeader->getHeader('FROM');
1536 $excerpt['__FIELD_REPLY_TO'] = $parsedHeader->getHeader('REPLY-TO');
1537 $excerpt['__FIELD_TO'] = $parsedHeader->getHeader('TO');
1538 $excerpt['__FIELD_CC'] = $parsedHeader->getHeader('CC');
1539 $excerpt['__FIELD_BCC'] = join(', ', array_merge(
1540 (array) $parsedHeader->getHeader('X-Original-Rcpt-to'),
1541 (array) $parsedHeader->getHeader('BCC')
1542 ));
1543 }
1544
1545 $excerpt[$field] = explode(',', $excerpt[$field]);
1546
1547 foreach ($excerpt[$field] as $k => $item)
1548 {
1549 unset($excerpt[$field][$k]);
1550
1551 $address = new Main\Mail\Address($item);
1552
1553 if ($address->validate())
1554 {
1555 if ($address->getName())
1556 {
1557 $excerpt[$field][] = sprintf(
1558 '%s <%s>',
1559 sprintf('=?%s?B?%s?=', SITE_CHARSET, base64_encode($address->getName())),
1560 $address->getEmail()
1561 );
1562 }
1563 else
1564 {
1565 $excerpt[$field][] = $address->getEmail();
1566 }
1567 }
1568 }
1569
1570 $excerpt[$field] = join(', ', $excerpt[$field]);
1571 }
1572
1573 $outgoingParams = [
1574 'CHARSET' => LANG_CHARSET,
1575 'CONTENT_TYPE' => 'html',
1576 'ATTACHMENT' => $attachments,
1577 'TO' => $excerpt['__FIELD_TO'],
1578 'SUBJECT' => $excerpt['__SUBJECT'],
1579 'BODY' => $outgoingBody,
1580 'HEADER' => [
1581 'From' => $excerpt['__FIELD_FROM'],
1582 'Reply-To' => $excerpt['__FIELD_REPLY_TO'],
1583 'Cc' => $excerpt['__FIELD_CC'],
1584 'Bcc' => $excerpt['__FIELD_BCC'],
1585 'Message-Id' => sprintf('<%s>', $excerpt['__MSG_ID']),
1586 ],
1587 ];
1588
1589 if (Option::get('mail', 'embed_local_id_in_outgoing_message_header', 'Y') == 'Y')
1590 {
1591 $outgoingParams['HEADER']['X-Bitrix-Mail-Message-UID'] = $excerpt['ID'];
1592 }
1593
1594 if(isset($excerpt['__IN_REPLY_TO']))
1595 {
1596 $outgoingParams['HEADER']['In-Reply-To'] = sprintf('<%s>', $excerpt['__IN_REPLY_TO']);
1597 }
1598
1600 $context->setCategory(Main\Mail\Context::CAT_EXTERNAL);
1601 $context->setPriority(Main\Mail\Context::PRIORITY_NORMAL);
1602
1604 $eventKey = $eventManager->addEventHandler(
1605 'main',
1606 'OnBeforeMailSend',
1607 function () use (&$excerpt)
1608 {
1609 if ($excerpt['UPLOAD_STAGE'] >= 2)
1610 {
1611 return new Main\EventResult(Main\EventResult::ERROR);
1612 }
1613 }
1614 );
1615
1616 $success = Main\Mail\Mail::send(array_merge(
1617 $outgoingParams,
1618 array(
1619 'TRACK_READ' => array(
1620 'MODULE_ID' => 'mail',
1621 'FIELDS' => array('msgid' => $excerpt['__MSG_ID']),
1622 'URL_PAGE' => '/pub/mail/read.php',
1623 ),
1624 //'TRACK_CLICK' => array(
1625 // 'MODULE_ID' => 'mail',
1626 // 'FIELDS' => array('msgid' => $excerpt['__MSG_ID']),
1627 //),
1628 'CONTEXT' => $context,
1629 )
1630 ));
1631
1632 $eventManager->removeEventHandler('main', 'OnBeforeMailSend', $eventKey);
1633
1634 if ($excerpt['UPLOAD_STAGE'] < 2 && !$success)
1635 {
1636 return false;
1637 }
1638
1639 $needUpload = true;
1640 if ($context->getSmtp() && $context->getSmtp()->getFrom() == $this->mailbox['EMAIL'])
1641 {
1642 $needUpload = !in_array('deny_upload', (array) $this->mailbox['OPTIONS']['flags']);
1643 }
1644
1645 if ($needUpload)
1646 {
1647 if ($excerpt['UPLOAD_STAGE'] < 2)
1648 {
1650 array(
1651 'ID' => $excerpt['ID'],
1652 'MAILBOX_ID' => $excerpt['MAILBOX_ID'],
1653 ),
1654 array(
1655 'SYNC_STAGE' => 2,
1656 'ATTEMPTS' => 1,
1657 )
1658 );
1659 }
1660
1661 class_exists('Bitrix\Mail\Helper');
1662
1663 $message = new Mail\DummyMail(array_merge(
1664 $outgoingParams,
1665 array(
1666 'HEADER' => array_merge(
1667 $outgoingParams['HEADER'],
1668 array(
1669 'To' => $outgoingParams['TO'],
1670 'Subject' => $outgoingParams['SUBJECT'],
1671 )
1672 ),
1673 )
1674 ));
1675
1676 if ($this->uploadMessage($message, $excerpt))
1677 {
1679 'ID' => $excerpt['ID'],
1680 'MAILBOX_ID' => $excerpt['MAILBOX_ID'],
1681 ));
1682 }
1683 }
1684 else
1685 {
1687 array(
1688 'ID' => $excerpt['ID'],
1689 'MAILBOX_ID' => $excerpt['MAILBOX_ID'],
1690 ),
1691 array(
1692 'SYNC_STAGE' => -1,
1693 'SYNC_LOCK' => 0,
1694 )
1695 );
1696 }
1697
1698 return;
1699 }
1700
1701 public function resyncMessage(array &$excerpt)
1702 {
1703 $body = $this->downloadMessage($excerpt);
1704 if (!empty($body))
1705 {
1706 return $this->cacheMessage(
1707 $body,
1708 array(
1709 'replaces' => $excerpt['ID'],
1710 )
1711 );
1712 }
1713
1714 return false;
1715 }
1716
1717 public function downloadAttachments(array &$excerpt)
1718 {
1719 $body = $this->downloadMessage($excerpt);
1720 if (!empty($body))
1721 {
1722 [,,, $attachments] = \CMailMessage::parseMessage($body, $this->mailbox['LANG_CHARSET']);
1723
1724 return $attachments;
1725 }
1726
1727 return false;
1728 }
1729
1731 {
1732 foreach ($this->getFilters() as $filter)
1733 {
1734 foreach ($filter['__actions'] as $action)
1735 {
1736 if (empty($action['LAZY_ATTACHMENTS']))
1737 {
1738 return false;
1739 }
1740 }
1741 }
1742
1743 return true;
1744 }
1745
1746 public function getFilters($force = false)
1747 {
1748 if (is_null($this->filters) || $force)
1749 {
1750 $this->filters = Mail\MailFilterTable::getList(array(
1751 'filter' => ORM\Query\Query::filter()
1752 ->where('ACTIVE', 'Y')
1753 ->where(
1754 ORM\Query\Query::filter()->logic('or')
1755 ->where('MAILBOX_ID', $this->mailbox['ID'])
1756 ->where('MAILBOX_ID', null)
1757 ),
1758 'order' => array(
1759 'SORT' => 'ASC',
1760 'ID' => 'ASC',
1761 ),
1762 ))->fetchAll();
1763
1764 foreach ($this->filters as $k => $item)
1765 {
1766 $this->filters[$k]['__actions'] = array();
1767
1768 $res = \CMailFilter::getFilterList($item['ACTION_TYPE']);
1769 while ($row = $res->fetch())
1770 {
1771 $this->filters[$k]['__actions'][] = $row;
1772 }
1773 }
1774 }
1775
1776 return $this->filters;
1777 }
1778
1782 public function resortTree($message = null)
1783 {
1784 global $DB;
1785
1786 $worker = function ($id, $msgId, &$i)
1787 {
1788 global $DB;
1789
1790 $stack = array(
1791 array(
1792 array($id, $msgId, false),
1793 ),
1794 );
1795
1796 $excerpt = array();
1797
1798 do
1799 {
1800 $level = array_pop($stack);
1801
1802 while ($level)
1803 {
1804 [$id, $msgId, $skip] = array_shift($level);
1805
1806 if (!$skip)
1807 {
1808 $excerpt[] = $id;
1809
1810 $DB->query(sprintf(
1811 'UPDATE b_mail_message SET LEFT_MARGIN = %2$u, RIGHT_MARGIN = %3$u WHERE ID = %1$u',
1812 $id, ++$i, ++$i
1813 ));
1814
1815 if (!empty($msgId))
1816 {
1817 $replies = array();
1818
1819 $res = Mail\MailMessageTable::getList(array(
1820 'select' => array(
1821 'ID',
1822 'MSG_ID',
1823 ),
1824 'filter' => array(
1825 '=MAILBOX_ID' => $this->mailbox['ID'],
1826 '=IN_REPLY_TO' => $msgId,
1827 ),
1828 'order' => array(
1829 'FIELD_DATE' => 'ASC',
1830 ),
1831 ));
1832
1833 while ($item = $res->fetch())
1834 {
1835 if (!in_array($item['ID'], $excerpt))
1836 {
1837 $replies[] = array($item['ID'], $item['MSG_ID'], false);
1838 }
1839 }
1840
1841 if ($replies)
1842 {
1843 array_unshift($level, array($id, $msgId, true));
1844
1845 array_push($stack, $level, $replies);
1846 $i--;
1847
1848 continue 2;
1849 }
1850 }
1851 }
1852 else
1853 {
1854 $DB->query(sprintf(
1855 'UPDATE b_mail_message SET RIGHT_MARGIN = %2$u WHERE ID = %1$u',
1856 $id, ++$i
1857 ));
1858 }
1859 }
1860 }
1861 while ($stack);
1862 };
1863
1864 if (!empty($message))
1865 {
1866 if (empty($message['ID']))
1867 {
1868 throw new Main\ArgumentException("Argument 'message' is not valid");
1869 }
1870
1871 $item = $DB->query(sprintf(
1872 'SELECT GREATEST(M1, M2) AS I FROM (SELECT
1873 (SELECT RIGHT_MARGIN FROM b_mail_message WHERE MAILBOX_ID = %1$u AND RIGHT_MARGIN > 0 ORDER BY LEFT_MARGIN ASC LIMIT 1) M1,
1874 (SELECT RIGHT_MARGIN FROM b_mail_message WHERE MAILBOX_ID = %1$u AND RIGHT_MARGIN > 0 ORDER BY LEFT_MARGIN DESC LIMIT 1) M2
1875 ) M',
1876 $this->mailbox['ID']
1877 ))->fetch();
1878
1879 $i = empty($item['I']) ? 0 : $item['I'];
1880
1881 $worker($message['ID'], $message['MSG_ID'], $i);
1882 }
1883 else
1884 {
1885 $DB->query(sprintf(
1886 'UPDATE b_mail_message SET LEFT_MARGIN = 0, RIGHT_MARGIN = 0 WHERE MAILBOX_ID = %u',
1887 $this->mailbox['ID']
1888 ));
1889
1890 $i = 0;
1891
1892 $res = $DB->query(sprintf(
1893 "SELECT ID, MSG_ID FROM b_mail_message M WHERE MAILBOX_ID = %u AND (
1894 IN_REPLY_TO IS NULL OR IN_REPLY_TO = '' OR NOT EXISTS (
1895 SELECT 1 FROM b_mail_message WHERE MAILBOX_ID = M.MAILBOX_ID AND MSG_ID = M.IN_REPLY_TO
1896 )
1897 )",
1898 $this->mailbox['ID']
1899 ));
1900
1901 while ($item = $res->fetch())
1902 {
1903 $worker($item['ID'], $item['MSG_ID'], $i);
1904 }
1905
1906 // crosslinked messages
1907 $query = sprintf(
1908 'SELECT ID, MSG_ID FROM b_mail_message
1909 WHERE MAILBOX_ID = %u AND LEFT_MARGIN = 0
1910 ORDER BY FIELD_DATE ASC LIMIT 1',
1911 $this->mailbox['ID']
1912 );
1913 while ($item = $DB->query($query)->fetch())
1914 {
1915 $worker($item['ID'], $item['MSG_ID'], $i);
1916 }
1917 }
1918 }
1919
1923 public function incrementTree($message)
1924 {
1925 if (empty($message['ID']))
1926 {
1927 throw new Main\ArgumentException("Argument 'message' is not valid");
1928 }
1929
1930 if (!empty($message['IN_REPLY_TO']))
1931 {
1932 $item = Mail\MailMessageTable::getList(array(
1933 'select' => array(
1934 'ID', 'MSG_ID', 'LEFT_MARGIN', 'RIGHT_MARGIN',
1935 ),
1936 'filter' => array(
1937 '=MAILBOX_ID' => $this->mailbox['ID'],
1938 '=MSG_ID' => $message['IN_REPLY_TO'],
1939 ),
1940 'order' => array(
1941 'LEFT_MARGIN' => 'ASC',
1942 ),
1943 ))->fetch();
1944
1945 if (!empty($item))
1946 {
1947 $message = $item;
1948
1949 $item = Mail\MailMessageTable::getList(array(
1950 'select' => array(
1951 'ID', 'MSG_ID',
1952 ),
1953 'filter' => array(
1954 '=MAILBOX_ID' => $this->mailbox['ID'],
1955 '<LEFT_MARGIN' => $item['LEFT_MARGIN'],
1956 '>RIGHT_MARGIN' => $item['RIGHT_MARGIN'],
1957 ),
1958 'order' => array(
1959 'LEFT_MARGIN' => 'ASC',
1960 ),
1961 'limit' => 1,
1962 ))->fetch();
1963
1964 if (!empty($item))
1965 {
1966 $message = $item;
1967 }
1968 }
1969 }
1970
1971 $this->resortTree($message);
1972 }
1973
1974 abstract public function checkMessagesForExistence($dirPath ='INBOX',$UIDs = []);
1975 abstract public function resyncIsOldStatus();
1976 abstract public function syncFirstDay();
1977 abstract protected function syncInternal();
1978 abstract public function listDirs($pattern, $useDb = false);
1979 abstract public function uploadMessage(Main\Mail\Mail $message, array &$excerpt);
1980 abstract public function downloadMessage(array &$excerpt);
1981 abstract public function syncMessages($mailboxID, $dirPath, $UIDs);
1982 abstract public function isAuthenticated();
1983
1984 public function getErrors()
1985 {
1986 return $this->errors;
1987 }
1988
1989 public function getWarnings()
1990 {
1991 return $this->warnings;
1992 }
1993
1994 public function getLastSyncResult()
1995 {
1996 return $this->lastSyncResult;
1997 }
1998
1999 protected function setLastSyncResult(array $data)
2000 {
2001 $this->lastSyncResult = array_merge($this->lastSyncResult, $data);
2002 }
2003
2004 public function getDirsHelper()
2005 {
2006 if (!$this->dirsHelper)
2007 {
2008 $this->dirsHelper = new Mail\Helper\MailboxDirectoryHelper($this->mailbox['ID']);
2009 }
2010
2011 return $this->dirsHelper;
2012 }
2013
2014 public function activateSync()
2015 {
2016 $options = $this->mailbox['OPTIONS'];
2017
2018 if (!isset($options['activateSync']) || $options['activateSync'] === true)
2019 {
2020 return false;
2021 }
2022
2023 $entity = MailboxTable::getEntity();
2024 $connection = $entity->getConnection();
2025
2026 $options['activateSync'] = true;
2027
2028 $query = sprintf(
2029 'UPDATE %s SET %s WHERE %s',
2030 $connection->getSqlHelper()->quote($entity->getDbTableName()),
2031 $connection->getSqlHelper()->prepareUpdate($entity->getDbTableName(), [
2032 'SYNC_LOCK' => 0,
2033 'OPTIONS' => serialize($options),
2034 ])[0],
2035 Query::buildFilterSql(
2036 $entity,
2037 [
2038 'ID' => $this->mailbox['ID']
2039 ]
2040 )
2041 );
2042
2043 return $connection->query($query);
2044 }
2045
2046 public function notifyNewMessages()
2047 {
2048 if (Loader::includeModule('im'))
2049 {
2051 $count = $lastSyncResult['newMessagesNotify'];
2052 $newMessageId = $lastSyncResult['newMessageId'];
2053 $message = null;
2054
2055 if ($count < 1)
2056 {
2057 return;
2058 }
2059
2060 if ($newMessageId > 0 && $count === 1)
2061 {
2062 $message = Mail\MailMessageTable::getByPrimary(
2063 $newMessageId,
2064 [
2065 'select' => [
2066 'ID',
2067 'HEADER',
2068 'FIELD_FROM',
2069 'FIELD_REPLY_TO',
2070 'FIELD_TO',
2071 'FIELD_CC',
2072 'FIELD_BCC',
2073 'BODY_HTML',
2074 'SUBJECT',
2075 ],
2076 'limit' => 1,
2077 ],
2078 )->fetch();
2079
2080 if (!empty($message))
2081 {
2082 Mail\Helper\Message::prepare($message);
2083 }
2084 }
2085
2087 $this->mailbox['USER_ID'],
2088 'new_message',
2089 array(
2090 'mailboxOwnerId' => $this->mailbox['USER_ID'],
2091 'mailboxId' => $this->mailbox['ID'],
2092 'count' => $count,
2093 'message' => $message,
2094 )
2095 );
2096 }
2097 }
2098
2105 public function isSupportSanitizeOnView(): bool
2106 {
2107 $supportedActionTypes = [
2108 // doesn't use BODY_HTML or BODY_BB
2109 "forumsocnet",
2110 "support",
2111 "crm",
2112 ];
2113 foreach ($this->getFilters() as $filter)
2114 {
2115 if (
2116 !in_array($filter['ACTION_TYPE'], $supportedActionTypes, true)
2117 && (
2118 $this->hasActionWithoutSanitizeSupport($filter['__actions'])
2119 || !empty($filter['PHP_CONDITION'])
2120 || !empty($filter['ACTION_PHP'])
2121 )
2122 )
2123 {
2124 return false;
2125 }
2126 }
2127 return true;
2128 }
2129
2137 private function hasActionWithoutSanitizeSupport($actions): bool
2138 {
2139 if (is_array($actions))
2140 {
2141 foreach ($actions as $action)
2142 {
2143 if (empty($action['SANITIZE_ON_VIEW']))
2144 {
2145 return true;
2146 }
2147 }
2148 }
2149 return false;
2150 }
2151
2152 /*
2153 Returns the minimum time between possible re-synchronization
2154 The time is taken from the option 'max_execution_time', but no more than static::SYNC_TIMEOUT
2155 */
2156 final public static function getTimeout()
2157 {
2158 return min(max(0, ini_get('max_execution_time')) ?: static::SYNC_TIMEOUT, static::SYNC_TIMEOUT);
2159 }
2160
2161 final public static function getForUserByEmail($email)
2162 {
2164 if (isset($mailbox['EMAIL']))
2165 {
2166 return static::createInstance($mailbox['ID'], false);
2167 }
2168
2169 return null;
2170 }
2171
2172 final public static function findBy($id, $email): ?Mailbox
2173 {
2174 $instance = null;
2175
2176 if ($id > 0)
2177 {
2179 {
2180 $instance = static::createInstance($mailbox['ID'], false);
2181 }
2182 }
2183
2184 if (!empty($email) && empty($instance))
2185 {
2186 $instance = static::getForUserByEmail($email);
2187 }
2188
2189 if (!empty($instance))
2190 {
2191 return $instance;
2192 }
2193
2194 return null;
2195 }
2196}
$connection
Определения actionsdefinitions.php:38
$count
Определения admin_tab.php:4
if(! $messageFields||!isset($messageFields['message_id'])||!isset($messageFields['status'])||!CModule::IncludeModule("messageservice")) $messageId
Определения callback_ismscenter.php:26
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static isCleanupOldEnabled()
Определения licensemanager.php:436
static checkTheMailboxForSyncAvailability(int $checkedMailboxId, ?int $ownerMailboxUserId=null)
Определения licensemanager.php:92
$lastSyncResult
Определения mailbox.php:43
linkMessage($uid, $id)
Определения mailbox.php:1312
reSyncStartPage()
Определения mailbox.php:534
static createInstance($id, $throw=true)
Определения mailbox.php:108
incrementTree($message)
Определения mailbox.php:1923
downloadAttachments(array &$excerpt)
Определения mailbox.php:1717
getMailbox()
Определения mailbox.php:388
resortTree($message=null)
Определения mailbox.php:1782
getSyncStatus()
Определения mailbox.php:714
createMessage(Main\Mail\Mail $message, array $fields=array())
Определения mailbox.php:1365
const NUMBER_OF_INCOMPLETE_MESSAGES_TO_REMOVE
Определения mailbox.php:28
__construct($mailbox)
Определения mailbox.php:346
getDirsMd5WithCounter($mailboxId)
Определения mailbox.php:113
activateSync()
Определения mailbox.php:2014
listDirs($pattern, $useDb=false)
uploadMessage(Main\Mail\Mail $message, array &$excerpt)
setSyncParams(array $params=array())
Определения mailbox.php:341
syncOutgoingMessage($excerpt)
Определения mailbox.php:1460
sendCountersEvent()
Определения mailbox.php:162
const MESSAGE_SET_OLD_STATUS_LIMIT_AT_A_TIME
Определения mailbox.php:26
updateGlobalCounterForCurrentUser()
Определения mailbox.php:441
findMessageInUploadQueue( $idFromHeaderMessage,)
Определения mailbox.php:1038
registerMessage(&$fields, $replaces=null, $isOutgoing=false, string $idFromHeaderMessage='', $redefineInsertDate=true, string $messageStatus=\Bitrix\Mail\MailMessageUidTable::DOWNLOADED)
Определения mailbox.php:1059
static instance(array $mailbox)
Определения mailbox.php:263
dismissOldMessages()
Определения mailbox.php:749
updateMessagesRegistry(array $filter, array $fields, $mailData=array())
Определения mailbox.php:1185
const MESSAGE_DELETION_LIMIT_AT_A_TIME
Определения mailbox.php:25
downloadMessage(array &$excerpt)
getDirsWithUnseenMailCounters()
Определения mailbox.php:178
const MESSAGE_RESYNCHRONIZATION_TIME
Определения mailbox.php:23
listMessages($params=array(), $fetch=true)
Определения mailbox.php:1020
resyncMessage(array &$excerpt)
Определения mailbox.php:1701
dismissDeletedUidMessages()
Определения mailbox.php:915
checkMessagesForExistence($dirPath='INBOX', $UIDs=[])
getMailboxOwnerId()
Определения mailbox.php:405
static getServices()
Определения mailbox.php:58
getFilters($force=false)
Определения mailbox.php:1746
setLastSyncResult(array $data)
Определения mailbox.php:1999
const NUMBER_OF_BROKEN_MESSAGES_TO_RESYNCHRONIZE
Определения mailbox.php:27
sync($syncCounters=true)
Определения mailbox.php:565
const INCOMPLETE_MESSAGE_REMOVE_TIMEOUT
Определения mailbox.php:24
cacheMessage(&$body, $params=array())
Определения mailbox.php:1327
const SYNC_TIME_QUOTA
Определения mailbox.php:22
getMailboxId()
Определения mailbox.php:393
isSupportLazyAttachments()
Определения mailbox.php:1730
syncMessages($mailboxID, $dirPath, $UIDs)
mail(array $params)
Определения mailbox.php:1343
isTimeQuotaExceeded()
Определения mailbox.php:421
notifyNewMessages()
Определения mailbox.php:2046
static getTimeout()
Определения mailbox.php:2156
static rawInstance($filter, $throw=true)
Определения mailbox.php:242
unregisterMessages($filter, $eventData=[], $ignoreDeletionCheck=false)
Определения mailbox.php:1200
updateGlobalCounter($userId)
Определения mailbox.php:431
normalizeMailboxOptions()
Определения mailbox.php:380
static isRuZone()
Определения mailbox.php:51
syncOutgoing()
Определения mailbox.php:1403
const SYNC_TIMEOUT
Определения mailbox.php:21
setCheckpoint()
Определения mailbox.php:426
static findBy($id, $email)
Определения mailbox.php:2172
const MAIL_SERVICES_ONLY_FOR_THE_RU_ZONE
Определения mailbox.php:30
getWarnings()
Определения mailbox.php:1989
static prepareMailbox($filter)
Определения mailbox.php:286
syncCounters()
Определения mailbox.php:553
restoringConsistency()
Определения mailbox.php:539
getLastSyncResult()
Определения mailbox.php:1994
getDirsHelper()
Определения mailbox.php:2004
static getForUserByEmail($email)
Определения mailbox.php:2161
$dirsMd5WithCounter
Определения mailbox.php:35
isSupportSanitizeOnView()
Определения mailbox.php:2105
pushSyncStatus($params, $force=false)
Определения mailbox.php:719
static add($userId, $type, $fields, $mailboxId=null)
Определения notification.php:175
static deleteList(array $filter)
Определения messagedeletequeue.php:38
const FIELD_SANITIZE_ON_VIEW
Определения mailmessage.php:34
static getPresetRemoveFilters()
Определения mailmessageuid.php:312
static deleteListSoft(array $filter)
Определения mailmessageuid.php:326
static deleteList(array $filter, array $messages=[], $limit=false, bool $sendEvent=true)
Определения mailmessageuid.php:113
static updateList(array $filter, array $fields, array $eventData=[], bool $sendEvent=true)
Определения mailmessageuid.php:71
static mergeData(array $insert, array $update)
Определения mailmessageuid.php:455
static getIconSrc($serviceName, $iconId=null)
Определения mailservices.php:185
static getMinSyncTime($mailboxId)
Определения mailboxdirectory.php:307
static getUserMailboxWithEmail($email)
Определения mailbox.php:58
static getUserMailbox($mailboxId, $userId=null)
Определения mailbox.php:122
static normalizeEmail(&$mailbox)
Определения mailbox.php:382
Определения result.php:20
static getInstance()
Определения eventmanager.php:31
static send($mailParams)
Определения mail.php:162
static getList(array $parameters=array())
Определения datamanager.php:431
static delete($primary)
Определения datamanager.php:1644
static add(array $data)
Определения datamanager.php:877
static update($primary, array $data)
Определения datamanager.php:1256
static send()
Определения event.php:387
static parseHeader($header, $charset)
Определения mail.php:1524
static parseMessage($message, $charset)
Определения mail.php:1560
static getImapData()
Определения domain2.php:10
$options
Определения commerceml2.php:49
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$entity
$uid
Определения hot_keys_act.php:8
$errors
Определения iblock_catalog_edit.php:74
$filter
Определения iblock_catalog_list.php:54
global $DB
Определения cron_frame.php:29
global $USER
Определения csv_new_run.php:40
$context
Определения csv_new_setup.php:223
$success
Определения mail_entry.php:69
const SITE_CHARSET
Определения include.php:62
const LANG_CHARSET
Определения include.php:65
$status
Определения session.php:10
Определения blacklist.php:3
Определения culture.php:9
Определения arrayresult.php:2
Определения ufield.php:9
Определения address.php:8
Определения chain.php:3
Определения collection.php:2
$service
Определения payment.php:18
$email
Определения payment.php:49
$message
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$instance
Определения ps_b24_final.php:14
$dir
Определения quickway.php:303
$i
Определения factura.php:643
</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."%"
Определения waybill.php:936
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
if(!Loader::includeModule('sale')) $pattern
Определения index.php:20
$matches
Определения index.php:22
$eventManager
Определения include.php:412
$contentId
Определения sonet_set_content_view.php:27
const SITE_ID
Определения sonet_set_content_view.php:12
$k
Определения template_pdf.php:567
$action
Определения file_dialog.php:21
$n
Определения update_log.php:107
$fields
Определения yandex_run.php:501