1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
mail.php
См. документацию.
1<?php
2
10
11IncludeModuleLangFile(__FILE__);
12
14$BX_MAIL_ERRORs = Array();
16
17class CMail
18{
19 const ERR_DEFAULT = 1;
20 const ERR_DB = 2;
21
22 const ERR_API_DEFAULT = 101;
23 const ERR_API_DENIED = 102;
28 const ERR_API_EMPTY_NAME = 107;
31 const ERR_API_BAD_NAME = 110;
34 const ERR_API_LONG_NAME = 113;
36 const ERR_API_OP_DENIED = 115;
37 const ERR_API_OLD_TOKEN = 116;
38
40 const ERR_API_BAD_DOMAIN = 202;
42
44
45 const F_DOMAIN_LOGO = 1;
46 const F_DOMAIN_REG = 2;
47
48 public static function getErrorMessage($code)
49 {
50 switch ($code)
51 {
52 case self::ERR_DB:
53 return GetMessage('MAIL_ERR_DB');
54 case self::ERR_API_DEFAULT:
55 return GetMessage('MAIL_ERR_API_DEFAULT');
56 case self::ERR_API_DENIED:
57 return GetMessage('MAIL_ERR_API_DENIED');
58 case self::ERR_API_NAME_OCCUPIED:
59 return GetMessage('MAIL_ERR_API_NAME_OCCUPIED');
60 case self::ERR_API_USER_NOTFOUND:
61 return GetMessage('MAIL_ERR_API_USER_NOTFOUND');
62 case self::ERR_API_EMPTY_DOMAIN:
63 return GetMessage('MAIL_ERR_API_EMPTY_DOMAIN');
64 case self::ERR_API_EMPTY_NAME:
65 return GetMessage('MAIL_ERR_API_EMPTY_NAME');
66 case self::ERR_API_EMPTY_PASSWORD:
67 return GetMessage('MAIL_ERR_API_EMPTY_PASSWORD');
68 case self::ERR_API_SHORT_PASSWORD:
69 return GetMessage('MAIL_ERR_API_SHORT_PASSWORD');
70 case self::ERR_API_BAD_NAME:
71 return GetMessage('MAIL_ERR_API_BAD_NAME');
72 case self::ERR_API_BAD_PASSWORD:
73 return GetMessage('MAIL_ERR_API_BAD_PASSWORD');
74 case self::ERR_API_PASSWORD_LIKELOGIN:
75 return GetMessage('MAIL_ERR_API_PASSWORD_LIKELOGIN');
76 case self::ERR_API_LONG_NAME:
77 return GetMessage('MAIL_ERR_API_LONG_NAME');
78 case self::ERR_API_LONG_PASSWORD:
79 return GetMessage('MAIL_ERR_API_LONG_PASSWORD');
80 case self::ERR_API_OP_DENIED:
81 return GetMessage('MAIL_ERR_API_OP_DENIED');
82 case self::ERR_API_OLD_TOKEN:
83 return getMessage('MAIL_ERR_API_OLD_TOKEN');
84 case self::ERR_API_DOMAIN_OCCUPIED:
85 return GetMessage('MAIL_ERR_API_DOMAIN_OCCUPIED');
86 case self::ERR_API_BAD_DOMAIN:
87 return GetMessage('MAIL_ERR_API_BAD_DOMAIN');
88 case self::ERR_API_PROHIBITED_DOMAIN:
89 return GetMessage('MAIL_ERR_API_PROHIBITED_DOMAIN');
90 case self::ERR_ENTRY_NOT_FOUND:
91 return GetMessage('MAIL_ERR_ENTRY_NOT_FOUND');
92 default:
93 return GetMessage('MAIL_ERR_DEFAULT');
94 }
95 }
96
97 public static function onUserUpdate($arFields)
98 {
99 if ($arFields['RESULT'] && isset($arFields['ACTIVE']) && $arFields['ACTIVE'] == 'N')
100 {
101 $selectResult = CMailbox::getList(array(), array('USER_ID' => intval($arFields['ID']), 'ACTIVE' => 'Y'));
102 while ($mailbox = $selectResult->fetch())
103 CMailbox::update($mailbox['ID'], array('ACTIVE' => 'N'));
104 }
105 }
106
107 public static function onUserDelete($id)
108 {
109 $selectResult = CMailbox::getList(array(), array('USER_ID' => intval($id)));
110 while ($mailbox = $selectResult->fetch())
111 CMailbox::delete($mailbox['ID']);
112 }
113
114 public static function option($name, $value = null)
115 {
116 static $options;
117
118 if (!is_scalar($name))
119 throw new \Bitrix\Main\ArgumentTypeException('name');
120
121 if (is_null($options))
122 $options = array();
123
124 if (is_null($value))
125 {
126 return array_key_exists($name, $options) ? $options[$name] : null;
127 }
128 else
129 {
130 $options[$name] = $value;
131 return $value;
132 }
133 }
134
135}
136
138{
139 public static function ResetErrors()
140 {
141 global $BX_MAIL_ERRORs;
142 $BX_MAIL_ERRORs = Array();
143 }
144
145 public static function SetError($ID, $TITLE="", $DESC="")
146 {
147 global $BX_MAIL_ERRORs;
148 $BX_MAIL_ERRORs[] = array("ID"=>$ID, "TITLE"=>$TITLE, "DESCRIPTION"=>$DESC);
149 return false;
150 }
151
152 public static function GetLastError($type=false)
153 {
154 global $BX_MAIL_ERRORs;
155 if($type===false)
158 }
159
160 public static function GetErrors()
161 {
162 global $BX_MAIL_ERRORs;
163 return $BX_MAIL_ERRORs;
164 }
165
166 public static function GetErrorsText($delim="<br>")
167 {
168 global $BX_MAIL_ERRORs;
169 $str = "";
170 foreach($BX_MAIL_ERRORs as $err)
171 {
172 if ($str!="")
173 $str .= $delim;
174 $str.=$err["TITLE"];
175 }
176 return $str;
177 }
178
179 public static function ErrCount()
180 {
181 global $BX_MAIL_ERRORs;
182 if(!is_array($BX_MAIL_ERRORs))
183 return 0;
184 return count($BX_MAIL_ERRORs);
185 }
186}
187
188
190{
192 {
193 parent::__construct($res);
194 }
195
196 function Fetch()
197 {
198 if($res = parent::Fetch())
199 {
200 if(!Bitrix\Main\Loader::includeModule('mail'))
201 {
202 return false;
203 }
204
205 $entity = \Bitrix\Mail\MailboxTable::getEntity();
206
207 foreach ($res as $alias => $value)
208 {
209 if (!$entity->hasField($alias))
210 {
211 continue;
212 }
213
214 foreach ($entity->getField($alias)->getFetchDataModifiers() as $modifier)
215 {
216 $res[$alias] = call_user_func_array($modifier, array($res[$alias], $this, $res, $alias));
217 }
218 }
219 }
220 return $res;
221 }
222}
223
224// class CMailBox
227{
228 var $pop3_conn = false;
229 var $mess_count = 0;
230 var $mess_size = 0;
231 var $resp = true;
232 var $last_result = true;
233 var $response = "";
235 public $mailbox_id = 0;
236 public $new_mess_count = 0;
238
239 public static function GetList($arOrder=[], $arFilter=[])
240 {
241 global $DB;
242 $strSql =
243 "SELECT MB.*, C.CHARSET as LANG_CHARSET, ".
244 " ".$DB->DateToCharFunction("MB.TIMESTAMP_X")." as TIMESTAMP_X ".
245 "FROM b_mail_mailbox MB, b_lang L, b_culture C ".
246 "WHERE MB.LID=L.LID AND C.ID=L.CULTURE_ID";
247
248 if(!is_array($arFilter))
249 {
250 $arFilter = [];
251 }
252
253 $arSqlSearch = [];
254 $filter_keys = array_keys($arFilter);
255 for($i = 0, $n = count($filter_keys); $i < $n; $i++)
256 {
257 $val = $arFilter[$filter_keys[$i]];
258
259 if (is_null($val) || $val === '')
260 {
261 continue;
262 }
263
264 $key = mb_strtoupper($filter_keys[$i]);
265
266 $strNegative = false;
267 if (mb_substr($key, 0, 1) == '!')
268 {
269 $key = mb_substr($key, 1);
270 $strNegative = 'Y';
271 }
272
273 $strExact = false;
274 if (mb_substr($key, 0, 1) == '=')
275 {
276 $key = mb_substr($key, 1);
277 $strExact = 'Y';
278 }
279
280 switch ($key)
281 {
282 case 'ID':
283 case 'PORT':
284 case 'DELETE_MESSAGES':
285 case 'ACTIVE':
286 case 'USE_MD5':
287 case 'RELAY':
288 case 'AUTH_RELAY':
289 $arSqlSearch[] = GetFilterQuery('MB.'.$key, ($strNegative == 'Y' ? '~' : '').$val, 'N');
290 break;
291 case 'LID':
292 case 'LOGIN':
293 case 'SERVER':
294 case 'NAME':
295 case 'DESCRIPTION':
296 case 'DOMAINS':
297 case 'SERVER_TYPE':
298 $arSqlSearch[] = GetFilterQuery('MB.'.$key, ($strNegative == 'Y' ? '~' : '').$val, $strExact == 'Y' ? 'N' : 'Y');
299 break;
300 case 'SERVICE_ID':
301 case 'USER_ID':
302 $arSqlSearch[] = 'MB.' . $key . ($strNegative == 'Y' ? ' != ' : ' = ') . intval($val);
303 break;
304 }
305 }
306
307 $is_filtered = false;
308 $strSqlSearch = "";
309 for($i = 0, $n = count($arSqlSearch); $i < $n; $i++)
310 {
311 if($arSqlSearch[$i] <> '')
312 {
313 $is_filtered = true;
314 $strSqlSearch .= " AND (".$arSqlSearch[$i].") ";
315 }
316 }
317
318 $arSqlOrder = Array();
319 foreach($arOrder as $by=>$order)
320 {
321 $order = mb_strtolower($order);
322 if ($order!="asc")
323 $order = "desc".($DB->type == "ORACLE"?" NULLS LAST":"");
324 else
325 $order = "asc".($DB->type == "ORACLE"?" NULLS FIRST":"");
326
327 switch(mb_strtoupper($by))
328 {
329 case "TIMESTAMP_X":
330 case "LID":
331 case "ACTIVE":
332 case "NAME":
333 case "SERVER":
334 case "PORT":
335 case "LOGIN":
336 case "USE_MD5":
337 case "DELETE_MESSAGES":
338 case "RELAY":
339 case "AUTH_RELAY":
340 case "SERVER_TYPE":
341 case "PERIOD_CHECK":
342 $arSqlOrder[] = " MB.".$by." ".$order." ";
343 break;
344 default:
345 $arSqlOrder[] = " MB.ID ".$order." ";
346 }
347 }
348
349 $strSqlOrder = "";
350 $arSqlOrder = array_unique($arSqlOrder);
351 DelDuplicateSort($arSqlOrder);
352
353 for ($i = 0, $n = count($arSqlOrder); $i < $n; $i++)
354 {
355 if($i==0)
356 $strSqlOrder = " ORDER BY ";
357 else
358 $strSqlOrder .= ",";
359
360 $strSqlOrder .= $arSqlOrder[$i];
361 }
362
363 $strSql .= $strSqlSearch.$strSqlOrder;
364
365 $res = $DB->Query($strSql);
366 $res = new _CMailBoxDBRes($res);
367 $res->is_filtered = $is_filtered;
368 return $res;
369 }
370
371 public static function GetByID($ID)
372 {
373 return CMailBox::GetList(Array(), Array("ID"=>$ID));
374 }
375
376 function CheckMail($mailbox_id = false)
377 {
378 global $DB;
379 $mbx = Array();
380 if($mailbox_id===false)
381 {
382 $strSql =
383 "SELECT MB.ID ".
384 "FROM b_mail_mailbox MB ".
385 "WHERE ACTIVE='Y' ";
386
387 $dbr = $DB->Query($strSql);
388 while($ar = $dbr->Fetch())
389 $mbx[] = $ar["ID"];
390 }
391 else
392 {
393 $mbx[] = $mailbox_id;
394 }
395
396 $bNoErrors = true;
397 foreach($mbx as $mailboxId)
398 {
399 $mb = new CMailbox();
400 if(!$mb->Connect($mailboxId))
401 {
402 $bNoErrors = false;
403 CMailError::SetError("ERR_CHECK_MAIL", GetMessage("MAIL_CL_ERR_CHECK_MAIL")." (mailbox id: ".$mailboxId.").", "");
404 }
405 }
406
407 return $bNoErrors;
408 }
409
410 public static function CheckMailAgent($ID)
411 {
412 global $DB, $USER;
413 $bUserCreated = false;
414 if (!isset($USER) || !is_object($USER))
415 {
416 $USER = new CUser();
417 $bUserCreated = true;
418 }
419 $ID = intval($ID);
420 $strSql =
421 "SELECT MB.ID, MB.PERIOD_CHECK ".
422 "FROM b_mail_mailbox MB ".
423 "WHERE ACTIVE='Y' ".
424 " AND ID=".$ID.
425 " AND USER_ID = 0";
426
427 $strReturn = '';
428 $dbr = $DB->Query($strSql);
429 if($ar = $dbr->Fetch())
430 {
431 $mb = new CMailbox();
432 $mb->Connect($ID);
433 if(intval($ar["PERIOD_CHECK"])>0)
434 $strReturn = "CMailbox::CheckMailAgent(".$ID.");";
435 }
436 if ($bUserCreated)
437 {
438 unset($USER);
439 }
440 return $strReturn;
441 }
442
443 public static function CheckFields($arFields, $ID=false)
444 {
445 global $APPLICATION;
446 $arMsg = array();
447
448 if (is_set($arFields, 'NAME') && mb_strlen($arFields['NAME']) < 1)
449 {
450 CMailError::SetError('B_MAIL_ERR_NAME', GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_NAME').'"');
451 $arMsg[] = array('id' => 'NAME', 'text' => GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_NAME').'"');
452 }
453
454 if (in_array(mb_strtolower($arFields['SERVER_TYPE']), array('pop3', 'imap', 'controller', 'domain', 'crdomain')) && is_set($arFields, 'LOGIN') && mb_strlen($arFields['LOGIN']) < 1)
455 {
456 CMailError::SetError('B_MAIL_ERR_LOGIN', GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_LOGIN').'"');
457 $arMsg[] = array('id' => 'LOGIN', 'text' => GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_LOGIN').'"');
458 }
459
460 if (in_array(mb_strtolower($arFields['SERVER_TYPE']), array('pop3', 'imap')) && is_set($arFields, 'PASSWORD') && mb_strlen($arFields['PASSWORD']) < 1)
461 {
462 CMailError::SetError('B_MAIL_ERR_PASSWORD', GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_PASSWORD').'"');
463 $arMsg[] = array('id' => 'PASSWORD', 'text' => GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_PASSWORD').'"');
464 }
465
466 if (in_array(mb_strtolower($arFields['SERVER_TYPE']), array('controller', 'domain', 'crdomain')) && is_set($arFields, 'USER_ID') && $arFields['USER_ID'] < 1)
467 {
468 CMailError::SetError('B_MAIL_ERR_USER_ID', GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_USER_ID').'"');
469 $arMsg[] = array('id' => 'USER_ID', 'text' => GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_USER_ID').'"');
470 }
471
472 if (in_array(mb_strtolower($arFields['SERVER_TYPE']), array('pop3', 'smtp', 'imap')) && is_set($arFields, 'SERVER') && mb_strlen($arFields['SERVER']) < 1)
473 {
474 CMailError::SetError('B_MAIL_ERR_SERVER_NAME', GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_SERVER').'"');
475 $arMsg[] = array('id' => 'SERVER', 'text' => GetMessage('MAIL_CL_ERR_NAME').' "'.GetMessage('MAIL_CL_SERVER').'"');
476 }
477 elseif (mb_strtolower($arFields['SERVER_TYPE']) == 'smtp')
478 {
479 $dbres = CMailBox::GetList(array(), array('ACTIVE' => 'Y', 'SERVER_TYPE' => 'smtp', 'SERVER' => $arFields['SERVER'], 'PORT' => $arFields['PORT']));
480 while($arres = $dbres->Fetch())
481 {
482 if ($ID === false || $arres['ID'] != $ID)
483 {
484 CMailError::SetError('B_MAIL_ERR_SERVER_NAME', GetMessage('B_MAIL_ERR_SN').' "'.GetMessage('MAIL_CL_SERVER').'"');
485 $arMsg[] = array('id' => 'SERVER', 'text' => GetMessage('B_MAIL_ERR_SN').' "'.GetMessage('MAIL_CL_SERVER').'"');
486 break;
487 }
488 }
489 }
490
491 if (is_set($arFields, 'LID'))
492 {
493 $r = CLang::GetByID($arFields['LID']);
494 if (!$r->Fetch())
495 {
496 CMailError::SetError('B_MAIL_ERR_BAD_LANG', GetMessage('MAIL_CL_ERR_BAD_LANG'));
497 $arMsg[] = array('id' => 'LID', 'text' => GetMessage('MAIL_CL_ERR_BAD_LANG'));
498 }
499 }
500 elseif ($ID === false)
501 {
502 CMailError::SetError('B_MAIL_ERR_BAD_LANG_NA', GetMessage('MAIL_CL_ERR_BAD_LANG_NX'));
503 $arMsg[] = array('id' => 'LID', 'text' => GetMessage('MAIL_CL_ERR_BAD_LANG_NX'));
504 }
505
506 if (in_array(mb_strtolower($arFields['SERVER_TYPE']), array('imap', 'controller', 'domain', 'crdomain')))
507 {
508 if (is_set($arFields, 'SERVICE_ID'))
509 {
510 if (!empty($arFields['LID']) || $ID)
511 {
512 $LID_tmp = $arFields['LID'];
513 if (empty($arFields['LID']))
514 {
515 $arMb_tmp = CMailBox::GetList(array(), array('ID' => $ID))->fetch();
516 $LID_tmp = $arMb_tmp['LID'];
517 }
518 $result = Bitrix\Mail\MailServicesTable::getList(array(
519 'filter' => array('=SITE_ID' => $LID_tmp, '=ID' => $arFields['SERVICE_ID'])
520 ));
521 if (!$result->fetch())
522 {
523 CMailError::SetError('B_MAIL_ERR_BAD_SERVICE_ID', GetMessage('MAIL_CL_ERR_BAD_SERVICE_ID'));
524 $arMsg[] = array('id' => 'SERVICE_ID', 'text' => GetMessage('MAIL_CL_ERR_BAD_SERVICE_ID'));
525 }
526 }
527 }
528 else if ($ID === false)
529 {
530 CMailError::SetError('B_MAIL_ERR_BAD_SERVICE_ID_NA', GetMessage('MAIL_CL_ERR_BAD_SERVICE_ID_NX'));
531 $arMsg[] = array('id' => 'SERVICE_ID', 'text' => GetMessage('MAIL_CL_ERR_BAD_SERVICE_ID_NX'));
532 }
533 }
534
535 if (!empty($arMsg))
536 {
537 $e = new CAdminException($arMsg);
538 $APPLICATION->ThrowException($e);
539 return false;
540 }
541
542 return true;
543 }
544
545 public static function Add($arFields)
546 {
547 global $DB;
549
550 $arFields = array_filter($arFields, 'is_set');
551
552 if($arFields["ACTIVE"] != "Y")
553 $arFields["ACTIVE"] = "N";
554
555 if($arFields["DELETE_MESSAGES"] != "Y")
556 $arFields["DELETE_MESSAGES"] = "N";
557
558 if($arFields["USE_MD5"] != "Y")
559 $arFields["USE_MD5"] = "N";
560
561 if ($arFields['USE_TLS'] != 'Y' && $arFields['USE_TLS'] != 'S')
562 $arFields["USE_TLS"] = "N";
563
564 if (!in_array($arFields["SERVER_TYPE"], array("pop3", "smtp", "imap", "controller", "domain", "crdomain")))
565 $arFields["SERVER_TYPE"] = "pop3";
566
567 if (!CMailBox::CheckFields($arFields))
568 return false;
569
570 $ID = \Bitrix\Mail\MailboxTable::add($arFields)->getId();
571 if ($arFields['ACTIVE'] == 'Y' && $arFields['USER_ID'] != 0)
572 {
573 CUserCounter::Clear($arFields['USER_ID'], 'mail_unseen', $arFields['LID']);
574 $mailboxSyncManager = new \Bitrix\Mail\Helper\Mailbox\MailboxSyncManager($arFields['USER_ID']);
575 $mailboxSyncManager->setDefaultSyncData($ID);
576 }
577 if (in_array($arFields['SERVER_TYPE'], array('imap', 'controller', 'domain', 'crdomain')))
578 {
579 \CAgent::addAgent(sprintf('Bitrix\Mail\Helper::syncMailboxAgent(%u);', $ID), 'mail', 'N', (int) $arFields['PERIOD_CHECK'] * 60);
580 \CAgent::addAgent(sprintf('Bitrix\Mail\Helper::cleanupMailboxAgent(%u);', $ID), 'mail', 'N', 3600 * 24);
581 }
582
583 if ($arFields['SERVER_TYPE'] == 'pop3' && (int) $arFields['PERIOD_CHECK'] > 0)
584 CAgent::addAgent(sprintf('CMailbox::CheckMailAgent(%u);', $ID), 'mail', 'N', (int) $arFields['PERIOD_CHECK']*60);
585
587 return $ID;
588 }
589
590 public static function Update($ID, $arFields)
591 {
592 $ID = intval($ID);
593
595
596 $arFields = array_filter($arFields, 'is_set');
597
598 if(is_set($arFields, "ACTIVE") && $arFields["ACTIVE"]!="Y")
599 $arFields["ACTIVE"]="N";
600
601 if(is_set($arFields, "DELETE_MESSAGES") && $arFields["DELETE_MESSAGES"]!="Y")
602 $arFields["DELETE_MESSAGES"]="N";
603
604 if(is_set($arFields, "USE_MD5") && $arFields["USE_MD5"]!="Y")
605 $arFields["USE_MD5"]="N";
606
607 if(is_set($arFields, 'USE_TLS') && $arFields['USE_TLS'] != 'Y' && $arFields['USE_TLS'] != 'S')
608 $arFields["USE_TLS"]="N";
609
610 if (is_set($arFields, "SERVER_TYPE") && !in_array($arFields["SERVER_TYPE"], array("pop3", "smtp", "imap", "controller", "domain", "crdomain")))
611 $arFields["SERVER_TYPE"] = "pop3";
612
613 if(!CMailBox::CheckFields($arFields, $ID))
614 return false;
615
616 $mbox = \Bitrix\Mail\MailboxTable::getRowById($ID);
617
618 $serverType = is_set($arFields, 'SERVER_TYPE') ? $arFields['SERVER_TYPE'] : $mbox['SERVER_TYPE'];
619 $periodCheck = is_set($arFields, 'PERIOD_CHECK') ? $arFields['PERIOD_CHECK'] : $mbox['PERIOD_CHECK'];
620
621 if (!empty($mbox))
622 {
623 $userChanged = isset($arFields['USER_ID']) && $mbox['USER_ID'] != $arFields['USER_ID'];
624 $siteChanged = isset($arFields['LID']) && $mbox['LID'] != $arFields['LID'];
625
626 if ($userChanged || $siteChanged)
627 {
628 if ($mbox['ACTIVE'] == 'Y')
629 {
630 if ($mbox['USER_ID'] > 0)
631 {
632 $mailboxSyncManager = new \Bitrix\Mail\Helper\Mailbox\MailboxSyncManager($mbox['USER_ID']);
633 $mailboxSyncManager->deleteSyncData($mbox['ID']);
634 }
635 }
636
637 $newActive = isset($arFields['ACTIVE']) ? $arFields['ACTIVE'] : $mbox['ACTIVE'];
638 if ($newActive == 'Y')
639 {
640 $newUserId = isset($arFields['USER_ID']) ? $arFields['USER_ID'] : $mbox['USER_ID'];
641 $newSiteId = isset($arFields['LID']) ? $arFields['LID'] : $mbox['LID'];
642
643 if ($newUserId > 0)
644 {
645 $mailboxSyncManager = new \Bitrix\Mail\Helper\Mailbox\MailboxSyncManager($newUserId);
646 $mailboxSyncManager->setDefaultSyncData($mbox['ID']);
647 }
648 }
649 }
650
651 if ($mbox['USER_ID'] != 0 || isset($arFields['USER_ID']) && $arFields['USER_ID'] != 0)
652 {
653 CUserCounter::Clear($mbox['USER_ID'], 'mail_unseen', $mbox['LID']);
654 if ($siteChanged)
655 CUserCounter::Clear($mbox['USER_ID'], 'mail_unseen', $arFields['LID']);
656
657 if ($userChanged)
658 {
659 CUserCounter::Clear($arFields['USER_ID'], 'mail_unseen', $mbox['LID']);
660 if (isset($arFields['LID']) && $mbox['LID'] != $arFields['LID'])
661 CUserCounter::Clear($arFields['USER_ID'], 'mail_unseen', $arFields['LID']);
662 }
663 }
664 }
665
666 \CAgent::removeAgent(sprintf('CMailbox::CheckMailAgent(%u);', $ID), 'mail');
667 \CAgent::removeAgent(sprintf('Bitrix\Mail\Helper::syncMailboxAgent(%u);', $ID), 'mail');
668 \CAgent::removeAgent(sprintf('Bitrix\Mail\Helper::cleanupMailboxAgent(%u);', $ID), 'mail');
669
670 \Bitrix\Mail\MailboxTable::update($ID, $arFields);
671
672 if (in_array($serverType, array('imap', 'controller', 'domain', 'crdomain')))
673 {
674 \CAgent::addAgent(sprintf('Bitrix\Mail\Helper::syncMailboxAgent(%u);', $ID), 'mail', 'N', (int) $periodCheck*60);
675 \CAgent::addAgent(sprintf('Bitrix\Mail\Helper::cleanupMailboxAgent(%u);', $ID), 'mail', 'N', 3600 * 24);
676 }
677
678 if ($serverType == 'pop3' && (int) $periodCheck > 0)
679 CAgent::addAgent(sprintf('CMailbox::CheckMailAgent(%u);', $ID), 'mail', 'N', (int) $periodCheck*60);
680
682 return true;
683 }
684
692 public static function Delete($ID)
693 {
694 global $DB;
695 $ID = intval($ID);
696
698 $db_msg = Bitrix\Mail\MailMessageTable::getList(array(
699 'select' => array('ID'),
700 'filter' => array('MAILBOX_ID' => $ID)
701 ));
702 while($msg = $db_msg->Fetch())
703 {
704 if(!CMailMessage::Delete($msg["ID"]))
705 return false;
706 }
707
708 $db_flt = CMailFilter::GetList(Array(), Array("MAILBOX_ID"=>$ID));
709 while($flt = $db_flt->Fetch())
710 {
711 if(!CMailFilter::Delete($flt["ID"]))
712 return false;
713 }
714
715 $db_mbox = \CMailbox::getList(array('ID' => $ID, 'ACTIVE' => 'Y'));
716 if ($mbox = $db_mbox->fetch())
717 {
718 if ($mbox['USER_ID'] > 0)
719 {
720 \CUserCounter::clear($mbox['USER_ID'], 'mail_unseen', $mbox['LID']);
721 $mailboxSyncManager = new \Bitrix\Mail\Helper\Mailbox\MailboxSyncManager($mbox['USER_ID']);
722 $mailboxSyncManager->deleteSyncData($ID);
723 }
724 }
725
726 \CAgent::removeAgent(sprintf('CMailbox::CheckMailAgent(%u);', $ID), 'mail');
727 \CAgent::removeAgent(sprintf('Bitrix\Mail\Helper::syncMailboxAgent(%u);', $ID), 'mail');
728 \CAgent::removeAgent(sprintf('Bitrix\Mail\Helper::cleanupMailboxAgent(%u);', $ID), 'mail');
729
730 $strSql = "DELETE FROM b_mail_log WHERE MAILBOX_ID=".$ID;
731 if(!$DB->Query($strSql, true))
732 return false;
733
734 $strSql = "DELETE FROM b_mail_message_uid WHERE MAILBOX_ID=".$ID;
735 if(!$DB->Query($strSql, true))
736 return false;
737
738 // @TODO: make a log optional
739 //AddMessage2Log("The mailbox $ID was deleted");
740
741 $strSql = "DELETE FROM b_mail_blacklist WHERE MAILBOX_ID=".$ID;
742 if(!$DB->Query($strSql, true))
743 return false;
744
745 \Bitrix\Mail\Internals\MailboxAccessTable::deleteByFilter(['=MAILBOX_ID' => $ID,]);
746 $DB->query(sprintf('DELETE FROM b_mail_mailbox_dir WHERE MAILBOX_ID = %u', $ID));
747 $DB->query(sprintf('DELETE FROM b_mail_counter WHERE MAILBOX_ID = %u', $ID));
748 $DB->query(sprintf('DELETE FROM b_mail_entity_options WHERE MAILBOX_ID = %u', $ID));
749
751
752 \Bitrix\Mail\MailboxTable::delete($ID);
753
754 return new CDBResult();
755 }
756
757 public static function SMTPReload()
758 {
759 global $CACHE_MANAGER;
760 $CACHE_MANAGER->Read(3600000, $cache_id = "smtpd_reload");
761 $CACHE_MANAGER->Set($cache_id, true);
762 }
763
764 function SendCommand($command)
765 {
766 //SSRF "filter"
767 $command = preg_replace("/[\\n\\r]/", "", $command);
768
769 fputs($this->pop3_conn, $command."\r\n");
770
771 if($this->mailbox_id>0)
772 {
774 Array(
775 "MAILBOX_ID"=>$this->mailbox_id,
776 "STATUS_GOOD"=>"Y",
777 "MESSAGE"=>"> ".nl2br(preg_replace("'PASS .*'", "PASS ******", $command))
778 )
779 );
780 }
781 $this->resp = true;
782 }
783
784 function GetResponse($bMultiline = false, $bSkipFirst = true)
785 {
786 if(!$this->resp) return false;
787 $this->resp = false;
788
789 socket_set_timeout($this->pop3_conn, 20);
790 $res = rtrim(fgets($this->pop3_conn, 1024), "\r\n");
791// socket_set_blocking($this->pop3_conn, false);
792// socket_set_blocking($this->pop3_conn, true);
793
794 $this->last_result = ($res[0]=="+");
795 $this->response = $res;
796
797 if($this->mailbox_id>0)
798 {
800 Array(
801 "MAILBOX_ID"=>$this->mailbox_id,
802 "STATUS_GOOD"=>($this->last_result?"Y":"N"),
803 "MESSAGE"=>"< ".$res
804 )
805 );
806 }
807
808 if($bMultiline && $res[0]=="+")
809 {
810 if($bSkipFirst)
811 $res = "";
812 else
813 $res .= "\r\n";
814
815 $s = fgets($this->pop3_conn, 1024);
816 while($s <> '' && $s!=".\r\n")
817 {
818 if(mb_substr($s, 0, 2) == "..")
819 $s = mb_substr($s, 1);
820 $res .= $s;
821 $s = fgets($this->pop3_conn, 1024);
822 }
823 }
824 $this->response_body = $res;
825 return $this->last_result;
826 }
827
829 {
831 }
832
834 {
836 }
837
838 function GetPassword($p)
839 {
840 }
841
842 function Check($server, $port, $use_tls, $login, $passw)
843 {
844 if (($use_tls == 'Y' || $use_tls == 'S') && !preg_match('#^(tls|ssl)://#', $server))
845 $server = 'ssl://' . $server;
846
847 $skip_cert = $use_tls != 'Y';
848
850 $pop3_conn = stream_socket_client(
851 sprintf('%s:%s', $server, $port),
852 $errno, $errstr,
853 COption::getOptionInt('mail', 'connect_timeout', B_MAIL_TIMEOUT),
854 STREAM_CLIENT_CONNECT,
855 stream_context_create(array('ssl' => array('verify_peer' => !$skip_cert, 'verify_peer_name' => !$skip_cert)))
856 );
857 if(!$pop3_conn)
858 return array(false, GetMessage("MAIL_CL_TIMEOUT")." $errstr ($errno)");
859
860 $this->GetResponse();
861 $greeting = $this->GetResponseString();
862
863 $this->SendCommand("USER ".$login);
864 if(!$this->GetResponse())
865 return array(false, GetMessage("MAIL_CL_ERR_USER").' ('.$this->GetResponseString().')');
866 $this->SendCommand("PASS ".$passw);
867 if(!$this->GetResponse())
868 return array(false, GetMessage("MAIL_CL_ERR_PASSWORD").' ('.$this->GetResponseString().')');
869
870 $this->SendCommand("STAT");
871
872 if(!$this->GetResponse())
873 return array(false, GetMessage("MAIL_CL_ERR_STAT").' ('.$this->GetResponseString().')');
874
875 $stat = trim($this->GetResponseBody());
876 $arStat = explode(" ", $stat);
877 return array(true, $arStat[1]);
878 }
879
881 {
882 global $DB;
883 $mailbox_id = intval($mailbox_id);
884 $strSql =
885 "SELECT MB.*, C.CHARSET as LANG_CHARSET ".
886 "FROM b_mail_mailbox MB, b_lang L, b_culture C ".
887 "WHERE MB.LID=L.LID AND C.ID=L.CULTURE_ID ".
888 " AND MB.ID=".$mailbox_id;
889 $dbr = $DB->Query($strSql);
890 $dbr = new _CMailBoxDBRes($dbr);
891 if(!$arMAILBOX_PARAMS = $dbr->Fetch())
892 return CMailError::SetError("ERR_MAILBOX_NOT_FOUND", GetMessage("MAIL_CL_ERR_MAILBOX_NOT_FOUND"), GetMessage("MAIL_CL_ERR_MAILBOX_NOT_FOUND"));
893
894 if ($arMAILBOX_PARAMS['SYNC_LOCK'] > time()-600)
895 return;
896
897 $DB->query('UPDATE b_mail_mailbox SET SYNC_LOCK = '.time().' WHERE ID = '.$mailbox_id);
898
899 $result = $this->_connect($mailbox_id, $arMAILBOX_PARAMS);
900
901 $DB->query('UPDATE b_mail_mailbox SET SYNC_LOCK = 0 WHERE ID = '.$mailbox_id);
902
903 return $result;
904 }
905
906 private function _connect($mailbox_id, $arMAILBOX_PARAMS)
907 {
908 global $DB;
909
910 @set_time_limit(0);
911
912 // https://support.google.com/mail/answer/47948
913 if ($arMAILBOX_PARAMS["SERVER"] == 'pop.gmail.com')
914 $arMAILBOX_PARAMS["LOGIN"] = 'recent:' . $arMAILBOX_PARAMS["LOGIN"];
915
916 $server = $arMAILBOX_PARAMS["SERVER"];
917 if (($arMAILBOX_PARAMS['USE_TLS'] == 'Y' || $arMAILBOX_PARAMS['USE_TLS'] == 'S') && !preg_match('#^(tls|ssl)://#', $server))
918 $server = 'ssl://' . $server;
919
920 $skip_cert = $arMAILBOX_PARAMS['USE_TLS'] != 'Y';
921
923 $pop3_conn = stream_socket_client(
924 sprintf('%s:%s', $server, $arMAILBOX_PARAMS["PORT"]),
925 $errno, $errstr,
926 COption::getOptionInt('mail', 'connect_timeout', B_MAIL_TIMEOUT),
927 STREAM_CLIENT_CONNECT,
928 stream_context_create(array('ssl' => array('verify_peer' => !$skip_cert, 'verify_peer_name' => !$skip_cert)))
929 );
930
932 Array(
933 "MAILBOX_ID"=>$mailbox_id,
934 "STATUS_GOOD"=>"Y",
935 "MESSAGE"=>GetMessage("MAIL_CL_CONNECT_TO")." ".$arMAILBOX_PARAMS["SERVER"]
936 )
937 );
938
939 if(!$pop3_conn || !is_resource($pop3_conn))
940 {
942 Array(
943 "MAILBOX_ID"=>$mailbox_id,
944 "STATUS_GOOD"=>"N",
945 "MESSAGE"=>GetMessage("MAIL_CL_TIMEOUT")
946 )
947 );
948 return CMailError::SetError("ERR_CONNECT_TIMEOUT", GetMessage("MAIL_CL_TIMEOUT"), "$errstr ($errno)");
949 }
950
951 $this->mailbox_id = $mailbox_id;
952 if($arMAILBOX_PARAMS["CHARSET"]!='')
953 $this->charset = $arMAILBOX_PARAMS["CHARSET"];
954 else
955 $this->charset = $arMAILBOX_PARAMS["LANG_CHARSET"];
956 $this->use_md5 = $arMAILBOX_PARAMS["USE_MD5"];
957
958 $session_id = md5(uniqid(""));
959 $this->GetResponse();
960 $greeting = $this->GetResponseString();
961
962 if($this->use_md5=="Y" && preg_match("'(<.+>)'", $greeting, $reg))
963 {
964 $this->SendCommand("APOP ".$arMAILBOX_PARAMS["LOGIN"]." ".md5($reg[1].$arMAILBOX_PARAMS["PASSWORD"]));
965 if(!$this->GetResponse())
966 return CMailError::SetError("ERR_AFTER_USER", GetMessage("MAIL_CL_ERR_APOP"), $this->GetResponseString());
967 }
968 else
969 {
970 $this->SendCommand("USER ".$arMAILBOX_PARAMS["LOGIN"]);
971 if(!$this->GetResponse())
972 return CMailError::SetError("ERR_AFTER_USER", GetMessage("MAIL_CL_ERR_USER"), $this->GetResponseString());
973 $this->SendCommand("PASS ".$arMAILBOX_PARAMS["PASSWORD"]);
974 if(!$this->GetResponse())
975 return CMailError::SetError("ERR_AFTER_PASS", GetMessage("MAIL_CL_ERR_PASSWORD"), $this->GetResponseString());
976 }
977
978 $this->SendCommand("STAT");
979 if(!$this->GetResponse())
980 return CMailError::SetError("ERR_AFTER_STAT", GetMessage("MAIL_CL_ERR_STAT"), $this->GetResponseString());
981
982 $stat = trim($this->GetResponseBody());
983 $arStat = explode(" ", $stat);
984 $this->mess_count = $arStat[1];
985 if($this->mess_count>0)
986 {
987 $this->mess_size = $arStat[2];
988 $arLIST = array();
989
990 if($arMAILBOX_PARAMS["MAX_MSG_SIZE"]>0)
991 {
992 $this->SendCommand("LIST");
993 if(!$this->GetResponse(true))
994 return CMailError::SetError("ERR_AFTER_LIST", "LIST command error", $this->GetResponseString());
995 $list = $this->GetResponseBody();
996 preg_match_all("'([0-9]+)[ ]+?(.+)'", $list, $arLIST_temp, PREG_SET_ORDER);
997
998 for($i = 0, $n = count($arLIST_temp); $i < $n; $i++)
999 $arLIST[intval($arLIST_temp[$i][1])] = intval($arLIST_temp[$i][2]);
1000 }
1001
1002 $this->SendCommand("UIDL");
1003 if(!$this->GetResponse(true))
1004 return CMailError::SetError("ERR_AFTER_UIDL", GetMessage("MAIL_CL_ERR_UIDL"), $this->GetResponseString());
1005
1006 $uidl = $this->GetResponseBody();
1007 preg_match_all("'([0-9]+)[ ]+?(.+)'", $uidl, $arUIDL_temp, PREG_SET_ORDER);
1008
1009 $arUIDL = array();
1010 $cnt = count($arUIDL_temp);
1011 for ($i = 0; $i < $cnt; $i++)
1012 $arUIDL[md5($arUIDL_temp[$i][2])] = $arUIDL_temp[$i][1];
1013
1014 $skipOldUIDL = $cnt < $this->mess_count;
1015 if ($skipOldUIDL)
1016 {
1017 AddMessage2Log(sprintf(
1018 "%s\n%s of %s",
1019 $this->response, $cnt, $this->mess_count
1020 ), 'mail');
1021 }
1022
1023 $arOldUIDL = array();
1024 if (count($arUIDL) > 0)
1025 {
1026 $strSql = 'SELECT ID FROM b_mail_message_uid WHERE MAILBOX_ID = ' . $mailbox_id;
1027 $db_res = $DB->query($strSql);
1028 while ($ar_res = $db_res->fetch())
1029 {
1030 if (isset($arUIDL[$ar_res['ID']]))
1031 unset($arUIDL[$ar_res['ID']]);
1032 else if (!$skipOldUIDL)
1033 $arOldUIDL[] = $ar_res['ID'];
1034 }
1035 }
1036
1037 while (count($arOldUIDL) > 0)
1038 {
1039 $ids = "'" . join("','", array_splice($arOldUIDL, 0, 1000)) . "'";
1040
1041 // @TODO: make a log optional
1042 /*$toLog = [
1043 'filter'=>'\CAllMailBox::_connect',
1044 'removedMessages'=>$ids,
1045 ];
1046 AddMessage2Log($toLog);*/
1047
1048 $strSql = 'DELETE FROM b_mail_message_uid WHERE MAILBOX_ID = ' . $mailbox_id . ' AND ID IN (' . $ids . ')';
1049 $DB->query($strSql);
1050 }
1051
1052 $this->new_mess_count = 0;
1053 $this->deleted_mess_count = 0;
1054 $session_id = md5(uniqid(""));
1055
1056 foreach($arUIDL as $msguid=>$msgnum)
1057 {
1058 if($arMAILBOX_PARAMS["MAX_MSG_SIZE"]<=0 || $arLIST[$msgnum]<=$arMAILBOX_PARAMS["MAX_MSG_SIZE"])
1059 $this->GetMessage($mailbox_id, $msgnum, $msguid, $session_id);
1060
1061 if($arMAILBOX_PARAMS["DELETE_MESSAGES"]=="Y")
1062 {
1063 $this->DeleteMessage($msgnum);
1064 $this->deleted_mess_count++;
1065 }
1066
1067 $this->new_mess_count++;
1068 if($arMAILBOX_PARAMS["MAX_MSG_COUNT"]>0 && $arMAILBOX_PARAMS["MAX_MSG_COUNT"]<=$this->new_mess_count)
1069 break;
1070 }
1071 }
1072
1073 $this->SendCommand("QUIT");
1074 if(!$this->GetResponse())
1075 return CMailError::SetError("ERR_AFTER_QUIT", GetMessage("MAIL_CL_ERR_DISCONNECT"), $this->GetResponseString());
1076
1077 fclose($pop3_conn);
1078 return true;
1079 }
1080
1081 function GetMessage($mailbox_id, $msgnum, $msguid, $session_id)
1082 {
1083 global $DB;
1084
1085 $this->SendCommand("RETR ".$msgnum);
1086 if(!$this->GetResponse(true))
1087 return CMailError::SetError("ERR_AFTER_RETR", GetMessage("MAIL_CL_ERR_RETR"), $this->GetResponseString());
1088
1089 $message = $this->GetResponseBody();
1090
1091 $strSql = "INSERT INTO b_mail_message_uid(ID, MAILBOX_ID, SESSION_ID, DATE_INSERT, MESSAGE_ID) VALUES('".$DB->ForSql($msguid)."', ".intval($mailbox_id).", '".$DB->ForSql($session_id)."', ".$DB->GetNowFunction().", 0)";
1092 $DB->Query($strSql);
1093
1094 $message_id = CMailMessage::AddMessage($mailbox_id, $message, $this->charset);
1095 if($message_id>0)
1096 {
1097 $strSql = "UPDATE b_mail_message_uid SET MESSAGE_ID = " . intval($message_id) . " WHERE ID = '" . $DB->forSql($msguid) . "' AND MAILBOX_ID = " . intval($mailbox_id);
1098 $DB->Query($strSql);
1099 }
1100 return $message_id;
1101 } // function GetMessage(...
1102
1103 /*********************************************************************
1104 *********************************************************************/
1105 function DeleteMessage($msgnum)
1106 {
1107 $this->SendCommand("DELE ".$msgnum);
1108 if(!$this->GetResponse())
1109 return CMailError::SetError("ERR_AFTER_DELE", GetMessage("MAIL_CL_ERR_DELE"), $this->GetResponseString());
1110 }
1111}
1112
1114// class CMailHeader
1117{
1118 var $arHeader = Array();
1119 var $arHeaderLines = Array();
1120 var $strHeader = "";
1121 var $bMultipart = false;
1123 public $content_id = '';
1124
1125 public static function ConvertHeader($encoding, $type, $str, $charset)
1126 {
1127 if(mb_strtoupper($type) == "B")
1128 $str = base64_decode($str);
1129 else
1130 $str = quoted_printable_decode(str_replace("_", " ", $str));
1131
1132 $str = Emoji::encode($str);
1133 $str = CMailUtil::ConvertCharset($str, $encoding, $charset);
1134
1135 return $str;
1136 }
1137
1138 function DecodeHeader($str, $charset_to, $charset_document)
1139 {
1140 do
1141 {
1142 $n = 0;
1143 $str = preg_replace('/(=\?.*?\?(?:B|Q)\?.*?\?=)\s+((?1))/i', '\1\2', $str, -1, $n);
1144 }
1145 while ($n > 0);
1146
1147 $handler = function ($m) use ($charset_to)
1148 {
1149 return \CMailHeader::convertHeader($m[1], $m[2], $m[3], $charset_to);
1150 };
1151
1152 $n = 0;
1153 $str = preg_replace_callback('/=\?(.*?)\?(B|Q)\?(.*?)\?=/i', $handler, $str, -1, $n);
1154
1155 if ($n == 0 && $charset_document <> '')
1156 {
1157 $str = \CMailUtil::convertCharset($str, $charset_document, $charset_to);
1158 }
1159
1160 return $str;
1161 }
1162
1163 function Parse($message_header, $charset)
1164 {
1165 $this->charset = defined('BX_MAIL_DEFAULT_CHARSET') && BX_MAIL_DEFAULT_CHARSET != '' ? BX_MAIL_DEFAULT_CHARSET : $charset;
1166 if(preg_match("'content-type:.*?charset\s*=\s*([^\r\n;]+)'is", $message_header, $res))
1167 $this->charset = mb_strtolower(trim($res[1], ' "'));
1168
1169 $message_header = preg_replace('/\r\n([\x20\t])/i', '\1', $message_header);
1170
1171 $ar_message_header_tmp = explode("\r\n", $message_header);
1172
1173 for ($i = 0, $num = count($ar_message_header_tmp); $i < $num; $i++)
1174 {
1175 $this->arHeaderLines[] = \CMailHeader::decodeHeader($ar_message_header_tmp[$i], $charset, $this->charset);
1176 }
1177
1178 $this->arHeader = Array();
1179 for($i = 0, $num = count($this->arHeaderLines); $i < $num; $i++)
1180 {
1181 $p = mb_strpos($this->arHeaderLines[$i], ":");
1182 if($p>0)
1183 {
1184 $header_name = mb_strtoupper(trim(mb_substr($this->arHeaderLines[$i], 0, $p)));
1185 $header_value = trim(mb_substr($this->arHeaderLines[$i], $p + 1));
1186 $this->arHeader[$header_name] = $header_value;
1187 }
1188 }
1189
1190 $full_content_type = $this->arHeader["CONTENT-TYPE"];
1191 if($full_content_type == '')
1192 $full_content_type = "text/plain";
1193
1194 if(!($p = mb_strpos($full_content_type, ";")))
1195 $p = mb_strlen($full_content_type);
1196
1197 $this->content_type = trim(mb_substr($full_content_type, 0, $p));
1198 if(mb_strpos(mb_strtolower($this->content_type), "multipart/") === 0)
1199 {
1200 $this->bMultipart = true;
1201 if (!preg_match("'boundary\s*=\s*(.+?);'i", $full_content_type, $res))
1202 preg_match("'boundary\s*=\s*(.+)'i", $full_content_type, $res);
1203
1204 $this->boundary = trim($res[1], '"');
1205 if($p = mb_strpos($this->content_type, "/"))
1206 $this->MultipartType = mb_substr($this->content_type, $p + 1);
1207 }
1208
1209 if($p < mb_strlen($full_content_type))
1210 {
1211 if(preg_match("'name\s*=\s*([^;]+)'i", $full_content_type, $res))
1212 {
1213 $this->filename = trim($res[1], '"');
1214 }
1215 }
1216
1217 $cd = $this->arHeader["CONTENT-DISPOSITION"] ?? '';
1218 if ($cd <> '')
1219 {
1220 if (preg_match("'filename\s*=\s*([^;]+)'i", $cd, $res))
1221 {
1222 // Handles the case where the filename is specified directly.
1223 // Example: Content-Disposition: attachment; filename = "example.txt"
1224
1225 $this->filename = trim($res[1], '"');
1226 }
1227 else if (preg_match("'filename\s*\*=\s*([^;]+)'i", $cd, $res))
1228 {
1229 // Handles the case where the filename is encoded with a specified charset.
1230 // Example: Content-Disposition: attachment; filename *= UTF-8''example.txt
1231
1232 [$fncharset, $fnstr] = preg_split("/'[^']*'/", trim($res[1], '"'));
1233 $this->filename = CMailUtil::ConvertCharset(rawurldecode($fnstr), $fncharset, $charset);
1234 }
1235 else if (preg_match("'filename\s*\*0=\s*([^;]+)'i", $cd, $res))
1236 {
1237 // Handles the case where the filename is split into multiple parts.
1238 // Example: Content-Disposition: attachment; filename *0= "example"; filename*1=".txt"
1239
1240 $this->filename = trim($res[1], '"');
1241
1242 $i = 0;
1243 while (preg_match("'filename\s*\*".(++$i)."=\s*([^;]+)'i", $cd, $res))
1244 {
1245 $this->filename .= trim($res[1], '"');
1246 }
1247 }
1248 else if (preg_match("'filename\s*\*0\*=\s*([^;]+)'i", $cd, $res))
1249 {
1250 // Handles the case where the filename is split into multiple parts and encoded with a specified charset.
1251 // Example: Content-Disposition: attachment; filename*0*=UTF-8''example; filename *1* =UTF-8''file.txt
1252
1253 $fnstr = trim($res[1], '"');
1254
1255 $i = 0;
1256 while (preg_match("'filename\s*\*".(++$i)."\*?\s*=([^;]+)'i", $cd, $res))
1257 {
1258 $fnstr .= trim($res[1], '"');
1259 }
1260
1261 [$fncharset, $fnstr] = preg_split("/'[^']*'/", $fnstr);
1262 if (!empty($fnstr))
1263 {
1264 $fnstr = rawurldecode($fnstr);
1265 $this->filename = $fncharset ? CMailUtil::convertCharset($fnstr, $fncharset, $charset) : $fnstr;
1266 }
1267 }
1268 }
1269
1270 if(isset($this->arHeader["CONTENT-ID"]) && $this->arHeader["CONTENT-ID"]!='')
1271 $this->content_id = trim($this->arHeader["CONTENT-ID"], '"<>');
1272
1273 $this->strHeader = implode("\r\n", $this->arHeaderLines);
1274
1275 return true;
1276 }
1277
1278 function IsMultipart()
1279 {
1280 return $this->bMultipart;
1281 }
1282
1283 function MultipartType()
1284 {
1285 return mb_strtolower($this->MultipartType);
1286 }
1287
1288 function GetBoundary()
1289 {
1290 return $this->boundary;
1291 }
1292
1294 {
1295 return isset($this->arHeader[mb_strtoupper($type)]) ? $this->arHeader[mb_strtoupper($type)] : '';
1296 }
1297}
1298
1299
1301{
1302
1303 function fetch()
1304 {
1305 if ($item = parent::fetch())
1306 {
1307 $item['OPTIONS'] = (array) @unserialize($item['OPTIONS'], ['allowed_classes' => false]);
1308 $item['FOR_SPAM_TEST'] = sprintf('%s %s', $item['HEADER'], $item['BODY_HTML'] ?: $item['BODY']);
1309 }
1310
1311 return $item;
1312 }
1313
1314}
1315
1317// class CMailMessage
1320{
1321 public const MAX_LENGTH_MESSAGE_BODY = 3000000;
1322
1323 public static function GetList($arOrder = Array(), $arFilter = Array(), $bCnt = false)
1324 {
1325 global $DB;
1326 $sum = "case when NEW_MESSAGE='Y' then 1 else 0 end";
1327
1328 $strSql =
1329 "SELECT ".
1330 ($bCnt?
1331 "COUNT('x') as CNT, SUM(".$sum.") as CNT_NEW, COUNT('x')-SUM(".$sum.") as CNT_OLD "
1332 :
1333 "MS.*, MB.NAME as MAILBOX_NAME, MB.LID, ".
1334 " ".$DB->DateToCharFunction("MS.DATE_INSERT")." as DATE_INSERT, ".
1335 " ".$DB->DateToCharFunction("MS.FIELD_DATE")." as FIELD_DATE "
1336 ).
1337 "FROM b_mail_message MS ".
1338 ($bCnt? "":" INNER JOIN b_mail_mailbox MB ON MS.MAILBOX_ID=MB.ID ");
1339
1340 $arSqlSearch = Array();
1341 $filter_keys = array_keys($arFilter);
1342 for($i = 0, $n = count($filter_keys); $i < $n; $i++)
1343 {
1344 $key = $filter_keys[$i];
1345 $val = $arFilter[$key];
1347 $key = mb_strtoupper($res["FIELD"]);
1348 $cOperationType = $res["OPERATION"];
1349
1350 if($cOperationType == "?")
1351 {
1352 if ($val == '') continue;
1353 switch($key)
1354 {
1355 case "ID":
1356 case "MAILBOX_ID":
1357 case "MSGUID":
1358 $arSqlSearch[] = GetFilterQuery("MS.".$key, $val, "N");
1359 break;
1360 case "FIELD_FROM":
1361 case "FIELD_TO":
1362 case "FIELD_CC":
1363 case "FIELD_BCC":
1364 $arSqlSearch[] = GetFilterQuery("MS.".$key, $val, "Y", Array("@", "_", ".", "-"));
1365 break;
1366 case "NEW_MESSAGE":
1367 case "SUBJECT":
1368 case "HEADER":
1369 case "MSG_ID":
1370 case "IN_REPLY_TO":
1371 case "BODY":
1372 $arSqlSearch[] = GetFilterQuery("MS.".$key, $val);
1373 break;
1374 case "SENDER":
1375 $arSqlSearch[] = GetFilterQuery("MS.FIELD_FROM", $val, "Y", array("@","_",".","-"));
1376 break;
1377 case "RECIPIENT":
1378 $arSqlSearch[] = GetFilterQuery("MS.FIELD_TO, MS.FIELD_CC, MS.FIELD_BCC", $val, "Y", array("@","_",".","-"));
1379 break;
1380 case "SPAM_RATING":
1382 $arSqlSearch[] = GetFilterQuery("MS.SPAM_RATING", $val, "N");
1383 break;
1384 case "SPAM":
1385 $arSqlSearch[] = GetFilterQuery("MS.SPAM", $val, "Y", array("?"));
1386 break;
1387 case "ALL":
1388 $arSqlSearch[] = GetFilterQuery("MS.HEADER, MS.BODY", $val);
1389 break;
1390 }
1391 }
1392 else
1393 {
1394 switch($key)
1395 {
1396 case "SPAM":
1397 case "NEW_MESSAGE":
1398 $arSqlSearch[] = CMailUtil::FilterCreate("MS.".$key, $val, "string_equal", $cOperationType);
1399 break;
1400 case "ID":
1401 case "MAILBOX_ID":
1402 $arSqlSearch[] = CMailUtil::FilterCreate("MS.".$key, $val, "number", $cOperationType);
1403 break;
1404 case "SUBJECT":
1405 case "HEADER":
1406 case "BODY":
1407 case "MSGUID":
1408 case "FIELD_FROM":
1409 case "FIELD_TO":
1410 case "FIELD_CC":
1411 case "MSG_ID":
1412 case "IN_REPLY_TO":
1413 case "FIELD_BCC":
1414 $arSqlSearch[] = CMailUtil::FilterCreate("MS.".$key, $val, "string", $cOperationType);
1415 break;
1416 case "SPAM_RATING":
1417 $arSqlSearch[] = CMailUtil::FilterCreate("MS.".$key, $val, "number", $cOperationType);
1419 break;
1420 /*
1421 case "TIMESTAMP_X":
1422 $arSqlSearch[] = CIBlock::FilterCreate("BE.TIMESTAMP_X", $val, "date", $cOperationType);
1423 break;
1424 */
1425 }
1426 }
1427 }
1428
1429 $is_filtered = false;
1430 $strSqlSearch = "";
1431 for($i = 0, $n = count($arSqlSearch); $i < $n; $i++)
1432 {
1433 if($arSqlSearch[$i] <> '')
1434 {
1435 $strSqlSearch .= " AND (".$arSqlSearch[$i].") ";
1436 $is_filtered = true;
1437 }
1438 }
1439 $arSqlOrder = Array();
1440 foreach($arOrder as $by=>$order)
1441 {
1442 $by = mb_strtolower($by);
1443 $order = mb_strtolower($order);
1444
1445 if ($order!="asc")
1446 $order = "desc".($DB->type == "ORACLE"?" NULLS LAST":"");
1447 else
1448 $order = "asc".($DB->type == "ORACLE"?" NULLS FIRST":"");
1449
1450 if ($by == "field_date") $arSqlOrder[] = " MS.FIELD_DATE ".$order." ";
1451 elseif ($by == "field_from") $arSqlOrder[] = " MS.FIELD_FROM ".$order." ";
1452 elseif ($by == "field_reply_to")$arSqlOrder[] = " MS.FIELD_REPLY_TO ".$order." ";
1453 elseif ($by == "field_to") $arSqlOrder[] = " MS.FIELD_TO ".$order." ";
1454 elseif ($by == "field_cc") $arSqlOrder[] = " MS.FIELD_CC ".$order." ";
1455 elseif ($by == "field_bcc") $arSqlOrder[] = " MS.FIELD_BCC ".$order." ";
1456 elseif ($by == "subject") $arSqlOrder[] = " MS.SUBJECT ".$order." ";
1457 elseif ($by == "attachments") $arSqlOrder[] = " MS.ATTACHMENTS ".$order." ";
1458 elseif ($by == "date_insert") $arSqlOrder[] = " MS.DATE_INSERT ".$order." ";
1459 elseif ($by == "msguid") $arSqlOrder[] = " MS.MSGUID ".$order." ";
1460 elseif ($by == "mailbox_id") $arSqlOrder[] = " MS.MAILBOX_ID ".$order." ";
1461 elseif ($by == "new_message") $arSqlOrder[] = " MS.NEW_MESSAGE ".$order." ";
1462 elseif ($by == "mailbox_name" && !$bCnt) $arSqlOrder[] = " MB.NAME ".$order." ";
1463 elseif ($by == "spam_rating")
1464 {
1465 $arSqlOrder[] = " MS.SPAM_RATING ".$order." "; CMailFilter::RecalcSpamRating();
1466 }
1467 else $arSqlOrder[] = " MS.ID ".$order." ";
1468 }
1469
1470 $strSqlOrder = "";
1471 $arSqlOrder = array_unique($arSqlOrder);
1472 DelDuplicateSort($arSqlOrder);
1473
1474 for ($i = 0, $n = count($arSqlOrder); $i < $n; $i++)
1475 {
1476 if($i==0)
1477 $strSqlOrder = " ORDER BY ";
1478 else
1479 $strSqlOrder .= ",";
1480
1481 $strSqlOrder .= $arSqlOrder[$i];
1482 }
1483
1484 $strSql .= " WHERE 1=1 ".$strSqlSearch.$strSqlOrder;
1485
1486 $dbr = $DB->Query($strSql);
1487 $dbr = new \CMailMessageDBResult($dbr);
1488 $dbr->is_filtered = $is_filtered;
1489 return $dbr;
1490 }
1491
1492 public static function GetByID($ID)
1493 {
1494 return CMailMessage::GetList(Array(), Array("=ID"=>$ID));
1495 }
1496
1497 public static function GetSpamRating($msgid, $arRow=false)
1498 {
1499 global $DB;
1500 $res = null;
1501
1502 if(!is_array($arRow))
1503 $res = $DB->Query("SELECT SPAM_RATING, SPAM_LAST_RESULT, HEADER, BODY_HTML, BODY FROM b_mail_message WHERE ID=".intval($msgid));
1504 else
1505 $ar = $arRow;
1506
1507 if(is_array($arRow) || $res && ($ar = $res->Fetch()))
1508 {
1509 if (empty($ar['FOR_SPAM_TEST']))
1510 {
1511 $ar['FOR_SPAM_TEST'] = sprintf('%s %s', $ar['HEADER'], $ar['BODY_HTML'] ?: $ar['BODY'] );
1512 }
1513
1514 if($ar["SPAM_LAST_RESULT"]=="Y")
1515 return $ar["SPAM_RATING"];
1516 $arSpam = CMailFilter::GetSpamRating($ar["FOR_SPAM_TEST"]);
1517 $num = Round($arSpam["RATING"], 4);
1518 $DB->Query("UPDATE b_mail_message SET SPAM_RATING=".$num.", SPAM_LAST_RESULT='Y', SPAM_WORDS='".$DB->ForSql($arSpam["WORDS"], 255)."' WHERE ID=".intval($msgid));
1519 return $num;
1520 }
1521 }
1522
1523
1524 public static function parseHeader($header, $charset)
1525 {
1526 $h = new CMailHeader();
1527 $h->parse($header, $charset);
1528 return $h;
1529 }
1530
1531 public static function decodeMessageBody($header, $body, $charset)
1532 {
1533 $encoding = mb_strtolower($header->GetHeader('CONTENT-TRANSFER-ENCODING'));
1534
1535 if ($encoding == 'base64')
1536 $body = base64_decode($body);
1537 elseif ($encoding == 'quoted-printable')
1538 $body = quoted_printable_decode($body);
1539 elseif ($encoding == 'x-uue')
1540 $body = CMailUtil::uue_decode($body);
1541
1542 $content_type = mb_strtolower($header->content_type);
1543 if (empty($header->filename) && !empty($header->charset))
1544 {
1545 if (preg_match('/plain|html|text/', $content_type) && !preg_match('/x-vcard|csv/', $content_type))
1546 {
1547 $body = Emoji::encode($body);
1548 $body = CMailUtil::convertCharset($body, $header->charset, $charset);
1549 }
1550 }
1551
1552 return array(
1553 'CONTENT-TYPE' => $content_type,
1554 'CONTENT-ID' => $header->content_id,
1555 'BODY' => $body,
1556 'FILENAME' => $header->filename
1557 );
1558 }
1559
1560 public static function parseMessage($message, $charset)
1561 {
1562 $headerP = strpos($message, "\r\n\r\n");
1563
1564 if (false === $headerP)
1565 {
1566 $rawHeader = '';
1567 $body = $message;
1568 }
1569 else
1570 {
1571 $rawHeader = substr($message, 0, $headerP);
1572 $body = substr($message, $headerP+4);
1573 }
1574
1575 $header = \CMailMessage::parseHeader($rawHeader, $charset);
1576
1577 $htmlBody = '';
1578 $textBody = '';
1579
1580 $parts = array();
1581
1582 if ($header->isMultipart())
1583 {
1584 $startP = 0;
1585 $startRegex = sprintf('/(^|\r\n)--%s\r\n/', preg_quote($header->getBoundary(), '/'));
1586 if (preg_match($startRegex, $body, $matches, PREG_OFFSET_CAPTURE))
1587 {
1588 $startP = $matches[0][1] + strlen($matches[0][0]);
1589 }
1590
1591 $endP = strlen($body);
1592 $endRegex = sprintf('/\r\n--%s--(\r\n|$)/', preg_quote($header->getBoundary(), '/'));
1593 if (preg_match($endRegex, $body, $matches, PREG_OFFSET_CAPTURE))
1594 {
1595 $endP = $matches[0][1];
1596 }
1597
1598 if (!($startP < $endP))
1599 {
1600 $startP = 0;
1601 }
1602
1603 $data = substr($body, $startP, $endP-$startP);
1604
1605 $isHtml = false;
1606 $rawParts = preg_split(sprintf('/\r\n--%s\r\n/', preg_quote($header->getBoundary(), '/')), $data);
1607 $tmpParts = array();
1608 foreach ($rawParts as $part)
1609 {
1610 if (substr($part, 0, 2) == "\r\n")
1611 $part = "\r\n" . $part;
1612
1613 [, $subHtml, $subText, $subParts] = CMailMessage::parseMessage($part, $charset);
1614
1615 if ($subHtml)
1616 $isHtml = true;
1617
1618 if ($subText)
1619 $tmpParts[] = array($subHtml, $subText);
1620
1621 $parts = array_merge($parts, $subParts);
1622 }
1623
1624 if (mb_strtolower($header->MultipartType()) == 'alternative')
1625 {
1626 $candidate = '';
1627
1628 foreach ($tmpParts as $part)
1629 {
1630 if ($part[0])
1631 {
1632 if (!$htmlBody || (mb_strlen($htmlBody) < mb_strlen($part[0])))
1633 {
1634 $htmlBody = $part[0];
1635 $candidate = $part[1];
1636 }
1637 }
1638 else
1639 {
1640 if (!$textBody || mb_strlen($textBody) < mb_strlen($part[1]))
1641 $textBody = $part[1];
1642 }
1643 }
1644
1645 if (!trim($textBody))
1646 $textBody = $candidate;
1647 }
1648 else
1649 {
1650 foreach ($tmpParts as $part)
1651 {
1652 if ($textBody)
1653 $textBody .= "\r\n\r\n";
1654 $textBody .= $part[1];
1655
1656 if ($isHtml)
1657 {
1658 if ($htmlBody)
1659 $htmlBody .= "\r\n\r\n";
1660
1661 $htmlBody .= $part[0] ?: $part[1];
1662 }
1663 }
1664 }
1665 }
1666 else
1667 {
1668 $bodyPart = CMailMessage::decodeMessageBody($header, $body, $charset);
1669 $contentType = mb_strtolower($bodyPart['CONTENT-TYPE']);
1670
1671 if (
1672 !$bodyPart['FILENAME']
1673 && (mb_strpos($contentType, 'text/') === 0)
1674 && ($contentType !== 'text/calendar')
1675 )
1676 {
1677 if ($contentType == 'text/html')
1678 {
1679 $htmlBody = $bodyPart['BODY'];
1680 $textBody = html_entity_decode(htmlToTxt($bodyPart['BODY']), ENT_QUOTES | ENT_HTML401, $charset);
1681 }
1682 else
1683 {
1684 $textBody = $bodyPart['BODY'];
1685 }
1686 }
1687 else
1688 {
1689 $parts[] = $bodyPart;
1690 }
1691 }
1692
1693 return array($header, $htmlBody, $textBody, $parts);
1694 }
1695
1696 public static function addMessage($mailboxId, $message, $charset, $params = array())
1697 {
1698 [$header, $html, $text, $attachments] = CMailMessage::parseMessage($message, $charset);
1699
1700 return static::saveMessage($mailboxId, $message, $header, $html, $text, $attachments, $params);
1701 }
1702
1703 public static function saveMessage(int $mailboxId, &$message, &$header, &$bodyHtml, &$bodyText, &$attachments, $params = array())
1704 {
1705 $obHeader = &$header;
1706 $message_body_html = &$bodyHtml;
1707 $message_body = &$bodyText;
1708 $arMessageParts = &$attachments;
1709
1710 $isStrippedTagsToBody = false;
1711 $isOriginalEmptyBody = empty(trim(strip_tags($message_body_html)));
1712
1713 if (self::isLongMessageBody($message_body))
1714 {
1715 [$message_body, $message_body_html] = self::prepareLongMessage($message_body, $message_body_html);
1716 }
1717
1718 if (
1719 (mb_strlen($message_body_html) > 0)
1720 && empty(trim(strip_tags($message_body_html)))
1721 )
1722 {
1723 $message_body_html = '';
1724 $isStrippedTagsToBody = true;
1725 }
1726
1727 $arFields = array(
1728 "MAILBOX_ID" => $mailboxId,
1729 "HEADER" => $obHeader->strHeader,
1730 "FIELD_DATE_ORIGINAL" => $obHeader->GetHeader("DATE"),
1731 "NEW_MESSAGE" => "Y",
1732 "FIELD_FROM" => $obHeader->GetHeader("FROM"),
1733 "FIELD_REPLY_TO" => $obHeader->GetHeader("REPLY-TO"),
1734 "FIELD_TO" => $obHeader->GetHeader("TO"),
1735 "FIELD_CC" => $obHeader->GetHeader("CC"),
1736 "FIELD_BCC" => ($obHeader->GetHeader('X-Original-Rcpt-to')!=''?$obHeader->GetHeader('X-Original-Rcpt-to').($obHeader->GetHeader("BCC")!=''?', ':''):'').$obHeader->GetHeader("BCC"),
1737 "MSG_ID" => trim($obHeader->GetHeader("MESSAGE-ID"), " <>"),
1738 "FIELD_PRIORITY" => intval($obHeader->GetHeader("X-PRIORITY")),
1739 "MESSAGE_SIZE" => $params['size']?: mb_strlen($message),
1740 "SUBJECT" => $obHeader->GetHeader("SUBJECT"),
1741 "BODY" => rtrim($message_body),
1742 'OPTIONS' => array(
1743 'attachments' => count($arMessageParts),
1744 'isStrippedTags' => $isStrippedTagsToBody,
1745 'isOriginalEmptyBody' => $isOriginalEmptyBody,
1746 ),
1747 MailMessageTable::FIELD_SANITIZE_ON_VIEW => (int)($params[MailMessageTable::FIELD_SANITIZE_ON_VIEW] ?? 0)
1748 );
1749
1750 if (
1751 ($arFields['OPTIONS']['attachments'] <= 0)
1752 && (empty($message_body) || empty($message_body_html))
1753 )
1754 {
1755 $arFields['OPTIONS']['isEmptyBody'] = 'Y';
1756 }
1757
1758 $inReplyTo = trim($obHeader->GetHeader("IN-REPLY-TO"), " <>");
1759
1760 if($inReplyTo !== '')
1761 {
1762 $arFields['IN_REPLY_TO'] = $inReplyTo;
1763 }
1764
1765 $datetime = preg_replace('/(?<=[\s\d])UT$/i', '+0000', $arFields['FIELD_DATE_ORIGINAL']);
1766 if (!(isset($params['replaces']) && $params['replaces'] > 0) || strtotime($datetime) || $params['timestamp'])
1767 {
1768 $timestamp = strtotime($datetime) ?: $params['timestamp'] ?: time();
1769 $arFields['FIELD_DATE'] = convertTimeStamp($timestamp + \CTimeZone::getOffset(), 'FULL');
1770 }
1771
1772 if (!empty($message) && \Bitrix\Main\Config\Option::get('mail', 'save_src', B_MAIL_SAVE_SRC) == 'Y')
1773 {
1774 $arFields['FULL_TEXT'] = $message;
1775 }
1776
1777 $forSpamTest = sprintf('%s %s', $arFields['HEADER'], $message_body_html ?: $message_body);
1778
1779 $arFields["SPAM"] = "?";
1780 if(COption::GetOptionString("mail", "spam_check", B_MAIL_CHECK_SPAM)=="Y")
1781 {
1782 $arSpam = \CMailFilter::getSpamRating($forSpamTest);
1783 $arFields["SPAM_RATING"] = $arSpam["RATING"];
1784 $arFields["SPAM_WORDS"] = $arSpam["WORDS"];
1785 $arFields["SPAM_LAST_RESULT"] = "Y";
1786 }
1787
1788 // @TODO: MAX_ALLOWED_PACKET
1790 $arFields['SEARCH_CONTENT'] = \Bitrix\Mail\Helper\Message::prepareSearchContent($arFields);
1791
1792 if (isset($params['replaces']) && $params['replaces'] > 0)
1793 {
1794 \CMailMessage::update($message_id = $params['replaces'], $arFields, $mailboxId);
1795 }
1796 else
1797 {
1798 if (isset($params['trackable']) && $params['trackable'])
1799 {
1800 $arFields['OPTIONS']['trackable'] = \Bitrix\Main\Config\Option::get('main', 'track_outgoing_emails_read', 'Y') == 'Y';
1801 }
1802
1803 $message_id = \CMailMessage::add($arFields, $mailboxId);
1804 }
1805
1806 if ($message_id > 0)
1807 {
1808 $arFields['ID'] = $message_id;
1809 $arFields['FOR_SPAM_TEST'] = $forSpamTest;
1810
1811 \CMailLog::addMessage(array(
1812 'MAILBOX_ID' => $mailboxId,
1813 'MESSAGE_ID' => $message_id,
1814 'STATUS_GOOD' => 'Y',
1815 'LOG_TYPE' => isset($params['replaces']) && $params['replaces'] > 0 ? 'RENEW_MESSAGE' : 'NEW_MESSAGE',
1816 'MESSAGE' => sprintf(
1817 '%s (%s)%s', $arFields['SUBJECT'], $arFields['MESSAGE_SIZE'],
1818 \Bitrix\Main\Config\Option::get('mail', 'spam_check', B_MAIL_CHECK_SPAM) == 'Y'
1819 ? sprintf(' [%.3f]', $arFields['SPAM_RATING']) : ''
1820 ),
1821 ));
1822
1823 //If the message is new. Not resynchronization
1824 if (!(isset($params['replaces']) && $params['replaces'] > 0))
1825 {
1830 MessageClosureTable::insertIgnoreFromSql(sprintf('VALUES (%1$u, %1$u)', $message_id));
1831
1836 if (isset($arFields['IN_REPLY_TO']) && isset($arFields['MSG_ID']) && (string)isset($arFields['IN_REPLY_TO']) !== $arFields['MSG_ID'])
1837 {
1838 self::makeMessageClosureChain($message_id, $mailboxId, (string)$arFields['IN_REPLY_TO']);
1839 }
1840
1841 static $cachedMailboxes = [];
1842
1843 if (!array_key_exists($mailboxId, $cachedMailboxes))
1844 {
1845 $cachedMailboxes[$mailboxId] = Bitrix\Mail\MailboxTable::getList([
1846 'select' => ['ID', 'USER_ID', 'OPTIONS'],
1847 'filter' => ['=ID' => $mailboxId, '=ACTIVE' => 'Y'],
1848 ])->fetch();
1849 }
1850
1851 $mailbox = $cachedMailboxes[$mailboxId];
1852
1853 if ($mailbox['USER_ID'] > 0)
1854 {
1856 MailContact::getContactsData($arFields['FIELD_TO'], $mailbox['USER_ID'], \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_TO),
1857 MailContact::getContactsData($arFields['FIELD_FROM'], $mailbox['USER_ID'], \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_FROM),
1858 MailContact::getContactsData($arFields['FIELD_CC'], $mailbox['USER_ID'], \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_CC),
1859 MailContact::getContactsData($arFields['FIELD_REPLY_TO'], $mailbox['USER_ID'], \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_REPLY_TO),
1860 MailContact::getContactsData($arFields['FIELD_BCC'], $mailbox['USER_ID'], \Bitrix\Mail\Internals\MailContactTable::ADDED_TYPE_BCC)
1861 ));
1862 }
1863 }
1864
1865 $atchCnt = 0;
1866 if (empty($params['lazy_attachments']) && \Bitrix\Main\Config\Option::get('mail', 'save_attachments', B_MAIL_SAVE_ATTACHMENTS) == 'Y')
1867 {
1868 foreach ($arMessageParts as $i => $part)
1869 {
1870 $attachFields = array(
1871 'MESSAGE_ID' => $message_id,
1872 'FILE_NAME' => $part['FILENAME'],
1873 'CONTENT_TYPE' => $part['CONTENT-TYPE'],
1874 'FILE_DATA' => $part['BODY'],
1875 'CONTENT_ID' => $part['CONTENT-ID'],
1876 );
1877
1878 $arMessageParts[$i]['ATTACHMENT-ID'] = \CMailMessage::addAttachment($attachFields);
1879 if (!$arMessageParts[$i]['ATTACHMENT-ID'])
1880 {
1881 \CMailMessage::delete($message_id);
1882 return false;
1883 }
1884
1885 $atchCnt++;
1886 }
1887 }
1888
1889 $arFields['ATTACHMENTS'] = $atchCnt;
1890
1891 if ($message_body_html)
1892 {
1893 if (isset($params[MailMessageTable::FIELD_SANITIZE_ON_VIEW])
1894 && $params[MailMessageTable::FIELD_SANITIZE_ON_VIEW])
1895 {
1896 $arFields['BODY_HTML'] = $message_body_html;
1897 }
1898 else
1899 {
1900 Ini::adjustPcreBacktrackLimit(strlen($message_body_html)*2);
1901
1902 $msg = array(
1903 'html' => $message_body_html,
1904 'attachments' => array(),
1905 );
1906 foreach ($arMessageParts as $part)
1907 {
1908 if (!(is_array($part) && $part['ATTACHMENT-ID'] > 0))
1909 {
1910 continue;
1911 }
1912
1913 $msg['attachments'][] = array(
1914 'contentId' => $part['CONTENT-ID'],
1915 'uniqueId' => sprintf('attachment_%u', $part['ATTACHMENT-ID']),
1916 );
1917 }
1918
1920
1921 $arFields['BODY_HTML'] = \Bitrix\Mail\Helper\Message::sanitizeHtml($message_body_html, true);
1922 }
1923
1924 foreach ($arMessageParts as $part)
1925 {
1926 if (!(is_array($part) && $part['ATTACHMENT-ID'] > 0))
1927 {
1928 continue;
1929 }
1930
1931 $arFields['BODY_HTML'] = \Bitrix\Mail\Helper\Message::replaceBodyInlineImgContentId(
1932 (string)$arFields['BODY_HTML'],
1933 (string)$part['CONTENT-ID'],
1934 $part['ATTACHMENT-ID'],
1935 );
1936 }
1937
1938 \CMailMessage::update($message_id, array('BODY_HTML' => $arFields['BODY_HTML']), $mailboxId);
1939 }
1940 else
1941 {
1942 self::addDefferedDownload($mailboxId, $message_id);
1943 }
1944
1945 if (!(isset($params['replaces']) && $params['replaces'] > 0))
1946 {
1947 $arFields['IS_OUTCOME'] = !empty($params['outcome']);
1948 $arFields['IS_DRAFT'] = !empty($params['draft']);
1949 $arFields['IS_TRASH'] = !empty($params['trash']);
1950 $arFields['IS_SPAM'] = !empty($params['spam']);
1951 $arFields['IS_SEEN'] = !empty($params['seen']);
1952 $arFields['IS_RECOVERED'] = !empty($params['recovered']);
1953 $arFields['MSG_HASH'] = $params['hash'];
1954
1955 if (!empty($params['excerpt']) && is_array($params['excerpt']))
1956 {
1957 $arFields = $arFields + $params['excerpt'];
1958 }
1959
1960 $messageBindings = array();
1961
1962 $eventKey = \Bitrix\Main\EventManager::getInstance()->addEventHandler(
1963 'mail',
1964 'onBeforeUserFieldSave',
1965 function (\Bitrix\Main\Event $event) use (&$messageBindings)
1966 {
1967 $params = $event->getParameters();
1968 $messageBindings[] = $params['entity_type'];
1969 }
1970 );
1971
1972 $arFieldsForFilter = $arFields;
1973
1974 foreach (['BODY','BODY_BB','BODY_HTML','SUBJECT'] as $key)
1975 {
1976 if(!empty($arFieldsForFilter[$key]))
1977 {
1978 $arFieldsForFilter[$key] = Emoji::decode($arFieldsForFilter[$key]);
1979 }
1980 }
1981
1982 \CMailFilter::filter($arFieldsForFilter, 'R');
1983
1984 \Bitrix\Main\EventManager::getInstance()->removeEventHandler('mail', 'onBeforeUserFieldSave', $eventKey);
1985
1986 $icalAccess = isset($mailbox['OPTIONS']['ical_access']) && ($mailbox['OPTIONS']['ical_access'] === 'Y');
1987 $event = new \Bitrix\Main\Event('mail', 'onMailMessageNew', [
1988 'message' => $arFields,
1989 'attachments' => $arMessageParts,
1990 'userId' => isset($mailbox['USER_ID']) ? $mailbox['USER_ID'] : null,
1991 'icalAccess' => $icalAccess
1992 ]);
1993 $event->send();
1994
1995 addEventToStatFile(
1996 'mail',
1997 sprintf(
1998 'add_%s_%s',
1999 (empty($arFields['IN_REPLY_TO']) ? 'message' : 'reply'),
2000 (empty($params['outcome']) ? 'incoming' : 'outgoing')
2001 ),
2002 join(',', array_unique(array_filter($messageBindings))),
2003 $arFields['MSG_ID']
2004 );
2005 }
2006 }
2007
2008 return $message_id;
2009 }
2010
2021 private static function makeMessageClosureChain(int $messageId, int $mailboxId, string $inReply): void
2022 {
2023 $helper = \Bitrix\Main\Application::getConnection()->getSqlHelper();
2024 MessageClosureTable::insertIgnoreFromSelect(sprintf("SELECT DISTINCT %u, C.PARENT_ID
2025 FROM b_mail_message M
2026 INNER JOIN b_mail_message_closure C ON M.ID = C.MESSAGE_ID
2027 WHERE M.MAILBOX_ID = %u AND M.MSG_ID = '%s'",
2028 $messageId,
2029 $mailboxId,
2030 $helper->forSql($inReply)));
2031 }
2032
2040 public static function Add($arFields, $mailboxID = false)
2041 {
2042 global $DB;
2043
2044 if (is_set($arFields, "NEW_MESSAGE") && $arFields["NEW_MESSAGE"] != "N")
2045 $arFields["NEW_MESSAGE"]="Y";
2046
2047 if (is_set($arFields, "FULL_TEXT") && !is_set($arFields, "MESSAGE_SIZE"))
2048 $arFields["MESSAGE_SIZE"] = mb_strlen($arFields["FULL_TEXT"]);
2049
2050 if (!is_set($arFields, "DATE_INSERT"))
2051 $arFields["~DATE_INSERT"] = $DB->GetNowFunction();
2052
2053 if (is_set($arFields, "FIELD_DATE_ORIGINAL") && !is_set($arFields, "FIELD_DATE"))
2054 {
2055 $datetime = preg_replace('/(?<=[\s\d])UT$/i', '+0000', $arFields['FIELD_DATE_ORIGINAL']);
2056 $timestamp = strtotime($datetime) ?: time();
2057 $arFields['FIELD_DATE'] = convertTimeStamp($timestamp + \CTimeZone::getOffset(), 'FULL');
2058 }
2059
2060 if (array_key_exists('SUBJECT', $arFields))
2061 {
2062 $arFields['SUBJECT'] = strval(mb_substr($arFields['SUBJECT'], 0, 255));
2063 }
2064
2065 if (array_key_exists('OPTIONS', $arFields))
2066 {
2067 $arFields['OPTIONS'] = serialize($arFields['OPTIONS']);
2068 }
2069
2070 $params = $DB->PrepareInsert("b_mail_message", $arFields);
2071 $sql = sprintf("INSERT INTO b_mail_message (%s) VALUES (%s)", $params[0], $params[1]);
2072 $length = strlen($sql);
2073
2074 if (!\CMailUtil::IsSizeAllowed($length))
2075 {
2076 $limit = \Bitrix\Main\Application::getConnection()->getMaxAllowedPacket() - 1;
2077 $trimLength = $length - $limit;
2078 self::trimContent($arFields, $trimLength, [['BODY_HTML', 'BODY'], 'SEARCH_CONTENT', 'HEADER']);
2079
2080 $params = $DB->PrepareInsert("b_mail_message", $arFields);
2081 $sql = sprintf("INSERT INTO b_mail_message (%s) VALUES (%s)", $params[0], $params[1]);
2082 }
2083
2084 $DB->Query($sql);
2085
2086 $ID = intval($DB->LastID());
2087
2088 static::saveForDeferredDownload($ID, $arFields, $mailboxID);
2089
2090 return $ID;
2091 }
2092
2093 private static function saveForDeferredDownload($ID, $arFields, $mailboxID)
2094 {
2095 if (
2096 $mailboxID !== false
2097 && is_set($arFields, 'BODY_HTML')
2098 && $arFields['BODY_HTML'] === ''
2099 && (!is_set($arFields, 'BODY') || $arFields['BODY'] === '')
2100 && !$arFields['OPTIONS']['isStrippedTags']
2101 )
2102 {
2103 self::addDefferedDownload($mailboxID, $ID);
2104 }
2105 }
2106
2107 private static function addDefferedDownload($mailboxID, $ID): void
2108 {
2109 \Bitrix\Mail\Internals\MailEntityOptionsTable::add([
2110 'MAILBOX_ID' => $mailboxID,
2111 'ENTITY_TYPE' => 'MESSAGE',
2112 'ENTITY_ID' => $ID,
2113 'PROPERTY_NAME' => 'UNSYNC_BODY',
2114 'DATE_INSERT' => new \Bitrix\Main\Type\DateTime(),
2115 'VALUE' => 'Y',
2116 ]);
2117 }
2118
2119 public static function Update($ID, $arFields, $mailboxID = false)
2120 {
2121 global $DB;
2122 $ID = intval($ID);
2123
2124 if (is_set($arFields, "FIELD_DATE_ORIGINAL") && !is_set($arFields, "FIELD_DATE"))
2125 {
2126 $datetime = preg_replace('/(?<=[\s\d])UT$/i', '+0000', $arFields['FIELD_DATE_ORIGINAL']);
2127 $timestamp = strtotime($datetime) ?: time();
2128 $arFields['FIELD_DATE'] = convertTimeStamp($timestamp + \CTimeZone::getOffset(), 'FULL');
2129 }
2130
2131 if (array_key_exists('SUBJECT', $arFields))
2132 {
2133 $arFields['SUBJECT'] = strval(mb_substr($arFields['SUBJECT'], 0, 255));
2134 }
2135
2136 if (array_key_exists('OPTIONS', $arFields))
2137 {
2138 $arFields['OPTIONS'] = serialize($arFields['OPTIONS']);
2139 }
2140
2141 $params = $DB->PrepareUpdate("b_mail_message", $arFields);
2142 $sql = sprintf("UPDATE b_mail_message SET %s WHERE ID=%s", $params, $ID);
2143 $length = strlen($sql);
2144
2145 if (!\CMailUtil::IsSizeAllowed($length))
2146 {
2147 $limit = \Bitrix\Main\Application::getConnection()->getMaxAllowedPacket() - 1;
2148 $trimLength = $length - $limit;
2149 self::trimContent($arFields, $trimLength, [['BODY_HTML', 'BODY'], 'SEARCH_CONTENT', 'HEADER']);
2150
2151 $params = $DB->PrepareUpdate("b_mail_message", $arFields);
2152 $sql = sprintf("UPDATE b_mail_message SET %s WHERE ID=%s", $params, $ID);
2153 }
2154
2155 $DB->Query($sql);
2156
2157 static::saveForDeferredDownload($ID, $arFields, $mailboxID);
2158
2159 return true;
2160 }
2161
2162 private static function trimContent(array &$fields, $trimLength, $filters)
2163 {
2164 foreach ($filters as $filter)
2165 {
2166 if (is_array($filter))
2167 {
2168 $filter = array_filter(
2169 $filter,
2170 function ($name) use ($fields)
2171 {
2172 return isset($fields[$name]);
2173 }
2174 );
2175
2176 $totalLength = array_reduce(
2177 $filter,
2178 function ($total, $name) use ($fields)
2179 {
2180 $length = strlen($fields[$name]);
2181 return $total + $length;
2182 },
2183 0
2184 );
2185
2186 if ($totalLength === 0)
2187 {
2188 continue;
2189 }
2190
2191 $overLength = 0;
2192
2193 foreach ($filter as $subFilter)
2194 {
2195 $length = strlen($fields[$subFilter]);
2196 $ratio = $length / $totalLength;
2197 $over = ceil($trimLength * $ratio);
2198 $newLength = $length - $over;
2199 $fields[$subFilter] = $newLength > 0 ? substr($fields[$subFilter], 0, $newLength) : '';
2200 $overLength += $newLength > 0 ? $over : $length;
2201 }
2202
2203 $trimLength -= $overLength;
2204 }
2205 else
2206 {
2207 if (isset($fields[$filter]))
2208 {
2209 $length = strlen($fields[$filter]);
2210 $newLength = $length - $trimLength;
2211 $fields[$filter] = $newLength > 0 ? substr($fields[$filter], 0, $newLength) : '';
2212 $trimLength -= $newLength > 0 ? $trimLength : $length;
2213 }
2214 }
2215
2216 if ($trimLength <= 0)
2217 {
2218 break;
2219 }
2220 }
2221
2222 return $fields;
2223 }
2224
2225 public static function Delete($id)
2226 {
2227 global $DB;
2228 $id = intval($id);
2229
2230 $res = $DB->query('SELECT FILE_ID FROM b_mail_msg_attachment WHERE MESSAGE_ID = '.$id);
2231 while ($file = $res->fetch())
2232 {
2233 if ($file['FILE_ID'])
2234 {
2235 CFile::delete($file['FILE_ID']);
2237 }
2238 }
2239
2240 $strSql = "DELETE FROM b_mail_msg_attachment WHERE MESSAGE_ID=".$id;
2241 $DB->Query($strSql);
2242
2243 $DB->query(sprintf('DELETE FROM b_mail_message_access WHERE MESSAGE_ID = %u', $id));
2244
2245 $DB->query(sprintf('DELETE FROM b_mail_message_closure WHERE MESSAGE_ID = %1$u OR PARENT_ID = %1$u', $id));
2246
2247 $strSql = "DELETE FROM b_mail_message WHERE ID=".$id;
2248 $DB->Query($strSql);
2249
2250 return true;
2251 }
2252
2253 public static function MarkAsSpam($ID, $bIsSPAM = true, $arRow = false)
2254 {
2255 global $DB;
2256 $res = null;
2257
2258 if(!is_array($arRow))
2259 $res = $DB->Query("SELECT SPAM, HEADER, BODY_HTML, BODY, MAILBOX_ID FROM b_mail_message WHERE ID=".intval($ID));
2260 else
2261 $ar = $arRow;
2262
2263 if(is_array($arRow) || $res && ($ar = $res->Fetch()))
2264 {
2265 if (empty($ar['FOR_SPAM_TEST']))
2266 {
2267 $ar['FOR_SPAM_TEST'] = sprintf('%s %s', $ar['HEADER'], $ar['BODY_HTML'] ?: $ar['BODY'] );
2268 }
2269
2270 if($bIsSPAM)
2271 {
2272 if($ar["SPAM"]!="Y")
2273 {
2274 if($ar["SPAM"]=="N")
2275 CMailFilter::DeleteFromSpamBase($ar["FOR_SPAM_TEST"], false);
2276 CMailFilter::MarkAsSpam($ar["FOR_SPAM_TEST"], true);
2277 CMailMessage::Update($ID, Array("SPAM"=>"Y"));
2278
2280 Array(
2281 "MAILBOX_ID"=>$ar["MAILBOX_ID"],
2282 "MESSAGE_ID"=>$ID,
2283 "LOG_TYPE"=>"SPAM"
2284 )
2285 );
2286 }
2287 }
2288 else
2289 {
2290 if($ar["SPAM"]!="N")
2291 {
2292 if($ar["SPAM"]=="Y")
2293 CMailFilter::DeleteFromSpamBase($ar["FOR_SPAM_TEST"], true);
2294 CMailFilter::MarkAsSpam($ar["FOR_SPAM_TEST"], false);
2295 CMailMessage::Update($ID, Array("SPAM"=>"N"));
2296
2298 Array(
2299 "MAILBOX_ID"=>$ar["MAILBOX_ID"],
2300 "MESSAGE_ID"=>$ID,
2301 "LOG_TYPE"=>"NOTSPAM"
2302 )
2303 );
2304 }
2305 }
2306 $DB->Query("UPDATE b_mail_message SET SPAM_LAST_RESULT='N' WHERE ID=".intval($ID));
2307 }
2308 }
2309
2310 public static function addAttachment($arFields)
2311 {
2312 global $DB;
2313
2314 $arFields['FILE_NAME'] = trim($arFields['FILE_NAME']);
2315
2316 $strSql = "SELECT ID, MAILBOX_ID, ATTACHMENTS FROM b_mail_message WHERE ID=".intval($arFields["MESSAGE_ID"]);
2317 $dbr = $DB->Query($strSql);
2318 if(!($dbr_arr = $dbr->Fetch()))
2319 return false;
2320
2321 $n = intval($dbr_arr["ATTACHMENTS"])+1;
2322 if (empty($arFields['FILE_NAME']))
2323 {
2324 $arFields['FILE_NAME'] = AttachmentHelper::generateFileName(
2325 $dbr_arr['MAILBOX_ID'],
2326 $dbr_arr['ID'],
2327 $n,
2328 $arFields['CONTENT_TYPE'],
2329 );
2330 }
2331
2332 if(is_set($arFields, "CONTENT_TYPE"))
2333 $arFields["CONTENT_TYPE"] = mb_strtolower($arFields["CONTENT_TYPE"]);
2334
2335 if(mb_strpos($arFields["CONTENT_TYPE"], "image/") === 0 && (!is_set($arFields, "IMAGE_WIDTH") || !is_set($arFields, "IMAGE_HEIGHT")) && is_set($arFields, "FILE_DATA"))
2336 {
2337 $filename = CTempFile::GetFileName(md5(uniqid("")).'.tmp');
2339 if(file_put_contents($filename, $arFields["FILE_DATA"]) !== false)
2340 {
2341 $img_arr = CFile::GetImageSize($filename);
2342 $arFields["IMAGE_WIDTH"] = $img_arr? $img_arr[0]: 0;
2343 $arFields["IMAGE_HEIGHT"] = $img_arr? $img_arr[1]: 0;
2344 }
2345 }
2346
2347 if(is_set($arFields, "FILE_DATA") && !is_set($arFields, "FILE_SIZE"))
2348 $arFields["FILE_SIZE"] = strlen($arFields["FILE_DATA"]);
2349
2350 $file = array(
2351 'name' => md5($arFields['FILE_NAME']),
2352 'size' => $arFields['FILE_SIZE'],
2353 'type' => $arFields['CONTENT_TYPE'],
2354 'content' => $arFields['FILE_DATA'],
2355 'MODULE_ID' => 'mail'
2356 );
2357
2358 if (!($file_id = CFile::saveFile($file, AttachmentHelper::generateMessageAttachmentPath())))
2359 {
2360 \CMail::option('attachment_failure', true);
2361 return false;
2362 }
2363
2364 \CMail::option('attachment_failure', false);
2365
2366 unset($arFields['FILE_DATA']);
2367 $arFields['FILE_ID'] = $file_id;
2368
2369 $ID = $DB->add('b_mail_msg_attachment', $arFields);
2370
2371 if ($ID > 0)
2372 {
2373 $strSql = 'UPDATE b_mail_message SET ATTACHMENTS = ' . $n . ' WHERE ID = ' . intval($arFields['MESSAGE_ID']);
2374 $DB->query($strSql);
2375
2376 try
2377 {
2379 'FILE_ID' => $arFields['FILE_ID'],
2380 'FILE_NAME' => $arFields['FILE_NAME'],
2381 'FILE_SIZE' => $arFields['FILE_SIZE'],
2382 ));
2383 }
2384 catch (\Exception $e)
2385 {
2386 Application::getInstance()->getExceptionHandler()->writeToLog($e);
2387 }
2388 }
2389
2390 return $ID;
2391 }
2392
2400 public static function isLongMessageBody(?string &$messageBody): bool
2401 {
2402 if (!$messageBody)
2403 {
2404 return false;
2405 }
2406
2407 return mb_strlen($messageBody) > self::getBodyMaxLength();
2408 }
2409
2415 public static function getBodyMaxLength(): int
2416 {
2417 $limit = (int)\Bitrix\Main\Config\Option::get('mail', '~max_email_body_length', false);
2418 return ($limit > 0) ? $limit : self::MAX_LENGTH_MESSAGE_BODY;
2419 }
2420
2426 private static function getClearBody(string $body): string
2427 {
2428 //todo merge with \Bitrix\Main\Mail\Mail::convertBodyHtmlToText
2429 // get <body> inner html if exists
2430 $innerBody = trim(preg_replace('/(.*?<body[^>]*>)(.*?)(<\/body>.*)/is', '$2', $body));
2431 $body = $innerBody ?: $body;
2432
2433 // modify links to text version
2434 $body = preg_replace_callback(
2435 "%<a[^>]*?href=(['\"])(?<href>[^\1]*?)(?1)[^>]*?>(?<text>.*?)<\/a>%ims",
2436 function ($matches)
2437 {
2438 $href = $matches['href'];
2439 $text = trim($matches['text']);
2440 if (!$href)
2441 {
2442 return $matches[0];
2443 }
2444 $text = strip_tags($text);
2445 return ($text ? "$text:" : '') ."\n$href\n";
2446 },
2447 $body
2448 );
2449
2450 // change <br> to new line
2451 $body = preg_replace('/<br(\s*)?\/?>/i', "\n", $body);
2452
2453 $body = preg_replace('|(<style[^>]*>)(.*?)(<\/style>)|isU', '', $body);
2454 $body = preg_replace('|(<script[^>]*>)(.*?)(<\/script>)|isU', '', $body);
2455
2456 // remove tags
2457 $body = strip_tags($body);
2458
2459 // format text to the left side
2460 $lines = [];
2461 foreach (explode("\n", trim($body)) as $line)
2462 {
2463 $lines[] = trim($line);
2464 }
2465
2466 // remove redundant new lines
2467 $body = preg_replace("/[\\n]{2,}/", "\n\n", implode("\n", $lines));
2468
2469 // remove redundant spaces
2470 $body = preg_replace("/[ \\t]{2,}/", " ", $body);
2471
2472 // decode html-entities
2473 return html_entity_decode($body);
2474 }
2475
2480 protected static function getTextHtmlBlock(string &$body): array
2481 {
2482 $messageBody = '';
2483 $messageBodyHtml = '';
2484
2485 $boundaryMatches = [];
2486 preg_match('/content-type: multipart\/mixed; [\s\n\t]*boundary=\"(?<boundary>[\w+-]+)\"/i', $body, $boundaryMatches);
2487 if (is_string($boundaryMatches['boundary']))
2488 {
2489 $parts = explode('--'. $boundaryMatches['boundary'][0], $body);
2490 if (count($parts) > 1)
2491 {
2492 foreach ($parts as $part)
2493 {
2494 preg_match('/content-type: (?<contentType>text\/[html|plain]+); *charset="(?<charset>\w+-\d)"/i', $part, $contentTypeMatches);
2495 if (isset($contentTypeMatches['contentType']))
2496 {
2497 preg_match('/content-transfer-encoding: (?<encode>[\w]+)/i', $part, $encodeMatches);
2498
2499 $partBody = self::getPartBody($part);
2500 if ($partBody === null)
2501 {
2502 continue;
2503 }
2504
2505 if ($encodeMatches['encode'] === 'base64')
2506 {
2507 $partBody = base64_decode($partBody);
2508 }
2509
2510 if ($contentTypeMatches['contentType'] === 'text/plain')
2511 {
2512 $messageBody = $partBody;
2513 }
2514
2515 if ($contentTypeMatches['contentType'] === 'text/html')
2516 {
2517 $messageBodyHtml = $partBody;
2518 }
2519 }
2520 }
2521 }
2522 }
2523
2524 return [$messageBody, $messageBodyHtml];
2525 }
2526
2532 public static function prepareLongMessage(&$messageBody, &$messageBodyHtml): array
2533 {
2534 if (mb_stripos($messageBody, '--- Below this line is a copy of the message'))
2535 {
2536 $mainBodyHtml = $mainBody = explode('--- Below this line is a copy of the message', $messageBody)[0];
2537 }
2538 elseif (mb_stripos($messageBodyHtml, '<blockquote'))
2539 {
2540 $mainBody = $mainBodyHtml = self::cutBlockQuote($messageBodyHtml);
2541 }
2542 elseif (mb_stripos($messageBodyHtml, htmlspecialcharsbx('<blockquote')))
2543 {
2544 $mainBody = $mainBodyHtml = self::cutBlockHtmlQuote($messageBodyHtml);
2545 }
2546 elseif (mb_stripos($messageBody, '<blockquote'))
2547 {
2548 $mainBody = $mainBodyHtml = self::cutBlockQuote($messageBody);
2549 }
2550 else
2551 {
2552 [$mainBody, $mainBodyHtml] = self::getTextHtmlBlock($messageBody);
2553 if ($mainBody === '' && $mainBodyHtml === '')
2554 {
2555 $limit = self::getBodyMaxLength();
2556 $mainBody = mb_substr($messageBody, 0, $limit);
2557 $mainBodyHtml = mb_substr($messageBodyHtml, 0, $limit);
2558 }
2559 }
2560
2561 $mainBody = (string) $mainBody;
2562 $mainBodyHtml = (string) $mainBodyHtml;
2563
2564 $mainBody = self::getClearBody($mainBody);
2565
2566 return [$mainBody, $mainBodyHtml];
2567 }
2568
2573 protected static function getPartBody(&$part): ?string
2574 {
2575 $itemParts = explode("\r\n\r\n", $part);
2576 if (!is_array($itemParts) && (count($itemParts) < 2))
2577 {
2578 $itemParts = explode("\n\n", $part);
2579 }
2580
2581 if (!is_array($itemParts) && (count($itemParts) < 2))
2582 {
2583 $itemParts = explode("\r\n\n\r\n", $part);
2584 }
2585
2586 if (!is_array($itemParts) && (count($itemParts) < 2))
2587 {
2588 $itemParts = explode("\n\n\n", $part);
2589 }
2590
2591 if (is_array($itemParts) && (count($itemParts) >= 2))
2592 {
2593 return $itemParts[1];
2594 }
2595
2596 return null;
2597 }
2598
2603 private static function cutBlockQuote(&$messageBodyHtml): array|string|null
2604 {
2605 return preg_replace('|(<blockquote([^>]*)>)(.*?)(<\/blockquote>)|isU', '', $messageBodyHtml);
2606 }
2607
2612 private static function cutBlockHtmlQuote(&$messageBodyHtml): string
2613 {
2614 $messageBody = htmlspecialcharsback($messageBodyHtml);
2615
2616 return htmlspecialcharsbx(self::cutBlockQuote($messageBody));
2617 }
2618}
2619
2620
2622{
2624 {
2625 parent::__construct($res);
2626 }
2627
2628 function fetch()
2629 {
2630 if (($res = parent::fetch()) && $res['FILE_ID'] > 0)
2631 {
2632 if ($file = \CFile::makeFileArray($res['FILE_ID']))
2633 {
2634 if (!empty($file['tmp_name']) && \Bitrix\Main\IO\File::isFileExists($file['tmp_name']))
2635 {
2636 $res['FILE_DATA'] = \Bitrix\Main\IO\File::getFileContents($file['tmp_name']);
2637 }
2638 else
2639 {
2640 $res['FILE_DATA'] = false;
2641 }
2642 }
2643 }
2644
2645 return $res;
2646 }
2647}
2648
2650{
2651 public static function GetList($arOrder=Array(), $arFilter=Array())
2652 {
2653 global $DB;
2654
2655 $strSql =
2656 "SELECT * ".
2657 "FROM b_mail_msg_attachment MA ";
2658
2659 $arSqlSearch = Array();
2660 foreach ($arFilter as $key => $val)
2661 {
2663 $key = mb_strtoupper($res["FIELD"]);
2664 $cOperationType = $res["OPERATION"];
2665
2666 if($cOperationType == "?")
2667 {
2668 if ($val == '') continue;
2669 switch($key)
2670 {
2671 case "ID":
2672 case "MESSAGE_ID":
2673 case "FILE_SIZE":
2674 case "IMAGE_WIDTH":
2675 case "IMAGE_HEIGHT":
2676 $arSqlSearch[] = GetFilterQuery("MA.".$key, $val, "N");
2677 break;
2678 case "FILE_NAME":
2679 case "FILE_DATA":
2680 $arSqlSearch[] = GetFilterQuery("MA.".$key, $val);
2681 break;
2682 case "CONTENT_TYPE":
2683 $arSqlSearch[] = GetFilterQuery("MA.".$key, $val, "Y", array("/"));
2684 break;
2685 }
2686 }
2687 else
2688 {
2689 switch($key)
2690 {
2691 case "ID":
2692 case "MESSAGE_ID":
2693 case "FILE_SIZE":
2694 case "IMAGE_WIDTH":
2695 case "IMAGE_HEIGHT":
2696 $arSqlSearch[] = CMailUtil::FilterCreate("MA.".$key, $val, "number", $cOperationType);
2697 break;
2698 case "FILE_NAME":
2699 case "CONTENT_TYPE":
2700 case "FILE_DATA":
2701 $arSqlSearch[] = CMailUtil::FilterCreate("MA.".$key, $val, "string", $cOperationType);
2702 break;
2703 }
2704 }
2705 }
2706
2707 $is_filtered = false;
2708 $strSqlSearch = "";
2709 for($i = 0, $n = count($arSqlSearch); $i < $n; $i++)
2710 {
2711 if($arSqlSearch[$i] <> '')
2712 {
2713 $strSqlSearch .= " AND (".$arSqlSearch[$i].") ";
2714 $is_filtered = true;
2715 }
2716 }
2717 $arSqlOrder = Array();
2718 foreach($arOrder as $by=>$order)
2719 {
2720 $by = mb_strtolower($by);
2721 $order = mb_strtolower($order);
2722
2723 if ($order!="asc")
2724 $order = "desc".($DB->type == "ORACLE"?" NULLS LAST":"");
2725 else
2726 $order = "asc".($DB->type == "ORACLE"?" NULLS FIRST":"");
2727
2728 if ($by == "message_id") $arSqlOrder[] = " MA.MESSAGE_ID ".$order." ";
2729 elseif ($by == "file_name") $arSqlOrder[] = " MA.FILE_NAME ".$order." ";
2730 elseif ($by == "file_size") $arSqlOrder[] = " MA.FILE_SIZE ".$order." ";
2731 elseif ($by == "content_type") $arSqlOrder[] = " MA.CONTENT_TYPE ".$order." ";
2732 elseif ($by == "image_width") $arSqlOrder[] = " MA.IMAGE_WIDTH ".$order." ";
2733 elseif ($by == "image_height") $arSqlOrder[] = " MA.IMAGE_HEIGHT ".$order." ";
2734 else $arSqlOrder[] = " MA.ID ".$order." ";
2735 }
2736
2737 $strSqlOrder = "";
2738 $arSqlOrder = array_unique($arSqlOrder);
2739 DelDuplicateSort($arSqlOrder);
2740
2741 for ($i = 0, $n = count($arSqlOrder); $i < $n; $i++)
2742 {
2743 if($i==0)
2744 $strSqlOrder = " ORDER BY ";
2745 else
2746 $strSqlOrder .= ",";
2747
2748 $strSqlOrder .= $arSqlOrder[$i];
2749 }
2750
2751 $strSql .= " WHERE 1=1 ".$strSqlSearch.$strSqlOrder;
2752 //echo "<pre>".$strSql."</pre>";
2753 $dbr = $DB->Query($strSql);
2754 $dbr = new _CMailAttachmentDBRes($dbr);
2755 $dbr->is_filtered = $is_filtered;
2756 return $dbr;
2757 }
2758
2759 public static function GetByID($ID)
2760 {
2761 return CMailAttachment::GetList(Array(), Array("=ID"=>$ID));
2762 }
2763
2764 function Delete($id)
2765 {
2766 global $DB;
2767 $id = intval($id);
2768
2769 $res = $DB->query('SELECT FILE_ID FROM b_mail_msg_attachment WHERE MESSAGE_ID = '.$id);
2770 while ($file = $res->fetch())
2771 {
2772 if ($file['FILE_ID'])
2773 {
2774 CFile::delete($file['FILE_ID']);
2776 }
2777 }
2778
2779 $strSql = "DELETE FROM b_mail_msg_attachment WHERE ID=".$id;
2780 $DB->Query($strSql);
2781 }
2782
2783 public static function getContents($attachment)
2784 {
2785 if (!is_array($attachment))
2786 {
2787 if ($res = CMailAttachment::getByID($attachment))
2788 $attachment = $res->fetch();
2789 }
2790
2791 if (is_array($attachment))
2792 {
2793 if (!empty($attachment['FILE_DATA']))
2794 return $attachment['FILE_DATA'];
2795
2796 if ($attachment['FILE_ID'] > 0)
2797 {
2798 if ($file = \CFile::makeFileArray($attachment['FILE_ID']))
2799 {
2800 return (!empty($file['tmp_name'])
2801 && \Bitrix\Main\IO\File::isFileExists($file['tmp_name']))
2802 ? \Bitrix\Main\IO\File::getFileContents($file['tmp_name'])
2803 : false;
2804 }
2805 }
2806 }
2807
2808 return false;
2809 }
2810}
2811
2813{
2814 public static function convertCharset($str, $from, $to)
2815 {
2816 if (!trim($str))
2817 return $str;
2818
2819 $from = trim(mb_strtolower($from));
2820 $to = trim(mb_strtolower($to));
2821
2822 $escape = function ($matches)
2823 {
2824 return isset($matches[2]) ? '?' : $matches[1];
2825 };
2826
2827 if ($from != $to)
2828 {
2829 if (in_array($from, array('utf-8', 'utf8')))
2830 {
2831 // escape all invalid (rfc-3629) utf-8 characters
2832 $str = preg_replace_callback('/
2833 ([\x00-\x7F]+
2834 |[\xC2-\xDF][\x80-\xBF]
2835 |\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]
2836 |\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})
2837 |([\x80-\xFF])
2838 /x', $escape, $str);
2839 }
2840
2841 if ($result = Bitrix\Main\Text\Encoding::convertEncoding($str, $from, $to))
2842 $str = $result;
2843 else
2844 addMessage2Log(sprintf('Failed to convert email part. (%s -> %s : %s)', $from, $to, $error));
2845 }
2846
2847 if (in_array($to, array('utf-8', 'utf8')))
2848 {
2849 // escape invalid (rfc-3629) and 4-bytes utf-8 characters
2850 $str = preg_replace_callback('/
2851 ([\x00-\x7F]+
2852 |[\xC2-\xDF][\x80-\xBF]
2853 |\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF])
2854 |([\x80-\xFF])
2855 /x', $escape, $str);
2856 }
2857
2858 return $str;
2859 }
2860
2861 public static function uue_decode($str)
2862 {
2863 preg_match("/begin [0-7]{3} .+?\r?\n(.+)?\r?\nend/is", $str, $reg);
2864
2865 $str = $reg[1];
2866 $res = '';
2867 $str = preg_split("/\r?\n/", trim($str));
2868 $strlen = count($str);
2869 $spaceCharOrd = 0;
2870
2871 for ($i = 0; $i < $strlen; $i++)
2872 {
2873 $pos = 1;
2874 $d = 0;
2875 $len= (((ord(mb_substr($str[$i], 0, 1)) -32) - $spaceCharOrd) & 077);
2876
2877 while (($d + 3 <= $len) AND ($pos + 4 <= mb_strlen($str[$i])))
2878 {
2879 $c0 = (ord(mb_substr($str[$i], $pos, 1)) ^ 0x20);
2880 $c1 = (ord(mb_substr($str[$i], $pos + 1, 1)) ^ 0x20);
2881 $c2 = (ord(mb_substr($str[$i], $pos + 2, 1)) ^ 0x20);
2882 $c3 = (ord(mb_substr($str[$i], $pos + 3, 1)) ^ 0x20);
2883 $res .= chr(((($c0 - $spaceCharOrd) & 077) << 2) | ((($c1 - $spaceCharOrd) & 077) >> 4)).
2884 chr(((($c1 - $spaceCharOrd) & 077) << 4) | ((($c2 - $spaceCharOrd) & 077) >> 2)).
2885 chr(((($c2 - $spaceCharOrd) & 077) << 6) | (($c3 - $spaceCharOrd) & 077));
2886
2887 $pos += 4;
2888 $d += 3;
2889 }
2890
2891 if (($d + 2 <= $len) && ($pos + 3 <= mb_strlen($str[$i])))
2892 {
2893 $c0 = (ord(mb_substr($str[$i], $pos, 1)) ^ 0x20);
2894 $c1 = (ord(mb_substr($str[$i], $pos + 1, 1)) ^ 0x20);
2895 $c2 = (ord(mb_substr($str[$i], $pos + 2, 1)) ^ 0x20);
2896 $res .= chr(((($c0 - $spaceCharOrd) & 077) << 2) | ((($c1 - $spaceCharOrd) & 077) >> 4)).
2897 chr(((($c1 - $spaceCharOrd) & 077) << 4) | ((($c2 - $spaceCharOrd) & 077) >> 2));
2898
2899 $pos += 3;
2900 $d += 2;
2901 }
2902
2903 if (($d + 1 <= $len) && ($pos + 2 <= mb_strlen($str[$i])))
2904 {
2905 $c0 = (ord(mb_substr($str[$i], $pos, 1)) ^ 0x20);
2906 $c1 = (ord(mb_substr($str[$i], $pos + 1, 1)) ^ 0x20);
2907 $res .= chr(((($c0 - $spaceCharOrd) & 077) << 2) | ((($c1 - $spaceCharOrd) & 077) >> 4));
2908 }
2909 }
2910
2911 return $res;
2912 }
2913
2914 public static function MkOperationFilter($key)
2915 {
2916 if(mb_substr($key, 0, 1) == "!")
2917 {
2918 $key = mb_substr($key, 1);
2919 $cOperationType = "N";
2920 }
2921 elseif(mb_substr($key, 0, 2) == ">=")
2922 {
2923 $key = mb_substr($key, 2);
2924 $cOperationType = "GE";
2925 }
2926 elseif(mb_substr($key, 0, 1) == ">")
2927 {
2928 $key = mb_substr($key, 1);
2929 $cOperationType = "G";
2930 }
2931 elseif(mb_substr($key, 0, 2) == "<=")
2932 {
2933 $key = mb_substr($key, 2);
2934 $cOperationType = "LE";
2935 }
2936 elseif(mb_substr($key, 0, 1) == "<")
2937 {
2938 $key = mb_substr($key, 1);
2939 $cOperationType = "L";
2940 }
2941 elseif(mb_substr($key, 0, 1) == "=")
2942 {
2943 $key = mb_substr($key, 1);
2944 $cOperationType = "E";
2945 }
2946 else
2947 $cOperationType = "?";
2948
2949 return Array("FIELD"=>$key, "OPERATION"=>$cOperationType);
2950 }
2951
2952 public static function FilterCreate($fname, $vals, $type, $cOperationType=false, $bSkipEmpty = true)
2953 {
2954 return CMailUtil::FilterCreateEx($fname, $vals, $type, $bFullJoin, $cOperationType, $bSkipEmpty);
2955 }
2956
2957 public static function FilterCreateEx($fname, $vals, $type, &$bFullJoin, $cOperationType=false, $bSkipEmpty = true)
2958 {
2959 global $DB;
2960 if(!is_array($vals))
2961 $vals=Array($vals);
2962
2963 if(count($vals)<1)
2964 return "";
2965
2966 if(is_bool($cOperationType))
2967 {
2968 if($cOperationType===true)
2969 $cOperationType = "N";
2970 else
2971 $cOperationType = "E";
2972 }
2973
2974 if($cOperationType=="G")
2975 $strOperation = ">";
2976 elseif($cOperationType=="GE")
2977 $strOperation = ">=";
2978 elseif($cOperationType=="LE")
2979 $strOperation = "<=";
2980 elseif($cOperationType=="L")
2981 $strOperation = "<";
2982 else
2983 $strOperation = "=";
2984
2985 $bFullJoin = false;
2986 $bWasLeftJoin = false;
2987
2988 $res = Array();
2989 for($i = 0, $n = count($vals); $i < $n; $i++)
2990 {
2991 $val = $vals[$i];
2992 if(!$bSkipEmpty || $val <> '' || (is_bool($val) && $val===false))
2993 {
2994 switch ($type)
2995 {
2996 case "string_equal":
2997 if($val == '')
2998 $res[] = ($cOperationType=="N"?"NOT":"")."(".$fname." IS NULL OR ".$DB->Length($fname)."<=0)";
2999 else
3000 $res[] = ($cOperationType=="N"?" ".$fname." IS NULL OR NOT ":"")."(".CIBlock::_Upper($fname).$strOperation.CIBlock::_Upper("'".$DB->ForSql($val)."'").")";
3001 break;
3002 case "string":
3003 if($val == '')
3004 $res[] = ($cOperationType=="N"?"NOT":"")."(".$fname." IS NULL OR ".$DB->Length($fname)."<=0)";
3005 else
3006 if($strOperation=="=")
3007 $res[] = ($cOperationType=="N"?" ".$fname." IS NULL OR NOT ":"")."(".($DB->type == "ORACLE"?CIBlock::_Upper($fname)." LIKE ".CIBlock::_Upper("'".$DB->ForSqlLike($val)."'")." ESCAPE '\\'" : $fname." ".($strOperation=="="?"LIKE":$strOperation)." '".$DB->ForSqlLike($val)."'").")";
3008 else
3009 $res[] = ($cOperationType=="N"?" ".$fname." IS NULL OR NOT ":"")."(".($DB->type == "ORACLE"?CIBlock::_Upper($fname)." ".$strOperation." ".CIBlock::_Upper("'".$DB->ForSql($val)."'")." " : $fname." ".$strOperation." '".$DB->ForSql($val)."'").")";
3010 break;
3011 case "date":
3012 if($val == '')
3013 $res[] = ($cOperationType=="N"?"NOT":"")."(".$fname." IS NULL)";
3014 else
3015 $res[] = ($cOperationType=="N"?" ".$fname." IS NULL OR NOT ":"")."(".$fname." ".$strOperation." ".$DB->CharToDateFunction($DB->ForSql($val), "FULL").")";
3016 break;
3017 case "number":
3018 if($val == '')
3019 $res[] = ($cOperationType=="N"?"NOT":"")."(".$fname." IS NULL)";
3020 else
3021 $res[] = ($cOperationType=="N"?" ".$fname." IS NULL OR NOT ":"")."(".$fname." ".$strOperation." '".DoubleVal($val)."')";
3022 break;
3023 case "number_above":
3024 if($val == '')
3025 $res[] = ($cOperationType=="N"?"NOT":"")."(".$fname." IS NULL)";
3026 else
3027 $res[] = ($cOperationType=="N"?" ".$fname." IS NULL OR NOT ":"")."(".$fname." ".$strOperation." '".$DB->ForSql($val)."')";
3028 break;
3029 }
3030
3031 // INNER JOIN on such conditions
3032 if($val <> '' && $cOperationType!="N")
3033 $bFullJoin = true;
3034 else
3035 $bWasLeftJoin = true;
3036 }
3037 }
3038
3039 $strResult = "";
3040 for($i = 0, $n = count($res); $i < $n; $i++)
3041 {
3042 if($i>0)
3043 $strResult .= ($cOperationType=="N"?" AND ":" OR ");
3044 $strResult .= "(".$res[$i].")";
3045 }
3046 if($strResult!="")
3047 $strResult = "(".$strResult.")";
3048
3049 if($bFullJoin && $bWasLeftJoin && $cOperationType!="N")
3050 $bFullJoin = false;
3051
3052 return $strResult;
3053 }
3054
3055 public static function ByteXOR($a, $b, $l)
3056 {
3057 $c = "";
3058 for ($i = 0; $i < $l; $i++)
3059 {
3060 if (isset($a[$i]) && isset($b[$i]))
3061 $c .= $a[$i] ^ $b[$i];
3062 }
3063
3064 return $c;
3065 }
3066
3067 public static function BinMD5($val)
3068 {
3069 return(pack("H*",md5($val)));
3070 }
3071
3072 public static function Decrypt($str, $key=false)
3073 {
3074 $res = '';
3075 if($key===false)
3076 $key = COption::GetOptionString("main", "pwdhashadd", "");
3077 $key1 = CMailUtil::BinMD5($key);
3078 $str = base64_decode($str);
3079 while (strlen($str) > 0)
3080 {
3081 $m = substr($str, 0, 16);
3082 $str = substr($str, 16);
3083 $m = CMailUtil::ByteXOR($m, $key1, 16);
3084 $res .= $m;
3085 $key1 = CMailUtil::BinMD5($key.$key1.$m);
3086 }
3087 return $res;
3088 }
3089
3090 public static function Crypt($str, $key=false)
3091 {
3092 $res = '';
3093 if($key===false)
3094 $key = COption::GetOptionString("main", "pwdhashadd", "");
3095 $key1 = CMailUtil::BinMD5($key);
3096 while (strlen($str) > 0)
3097 {
3098 $m = substr($str, 0, 16);
3099 $str = substr($str, 16);
3100 $res .= CMailUtil::ByteXOR($m, $key1, 16);
3101 $key1 = CMailUtil::BinMD5($key.$key1.$m);
3102 }
3103 return(base64_encode($res));
3104 }
3105
3106 public static function extractAllMailAddresses($emails)
3107 {
3108 $result = array();
3109 $arEMails = explode(",", $emails);
3110 foreach($arEMails as $mail)
3111 {
3113 }
3114 return $result;
3115 }
3116
3117
3118 public static function ExtractMailAddress($email)
3119 {
3120 $email = trim($email);
3121 if(($pos = mb_strpos($email, "<"))!==false)
3122 $email = mb_substr($email, $pos + 1);
3123 if(($pos = mb_strpos($email, ">"))!==false)
3124 $email = mb_substr($email, 0, $pos);
3125 return mb_strtolower($email);
3126 }
3127
3128 public static function checkImapMailbox($server, $port, $use_tls, $login, $password, &$error)
3129 {
3130 $use_tls = is_string($use_tls) ? $use_tls : ($use_tls ? 'Y' : 'N');
3131
3132 $imap = new \Bitrix\Mail\Imap(
3133 $server, $port,
3134 $use_tls == 'Y' || $use_tls == 'S',
3135 $use_tls == 'Y',
3137 'UTF-8'
3138 );
3139
3140 if (($unseen = $imap->getUnseen('INBOX', $error)) === false)
3141 return (-1);
3142
3143 $error = false;
3144 return $unseen;
3145 }
3146}
3147
3148
3150$BX_MAIL_FILTER_CACHE = Array();
3151$BX_MAIL_SPAM_CNT = Array();
3152
3154{
3155 public static function GetList($arOrder=Array(), $arFilter=Array(), $bCnt=false)
3156 {
3157 global $DB;
3158 $strSql =
3159 "SELECT ".
3160 ($bCnt
3161 ?
3162 " COUNT('x') as CNT "
3163 :
3164 " MF.*, MB.NAME as MAILBOX_NAME, MB.ID as MAILBOX_ID, MB.SERVER_TYPE as MAILBOX_TYPE, MB.DOMAINS as DOMAINS, ".
3165 " ".$DB->DateToCharFunction("MF.TIMESTAMP_X")." as TIMESTAMP_X "
3166 ).
3167 " ".
3168 "FROM b_mail_mailbox MB ".(isset($arFilter["EMPTY"]) && $arFilter["EMPTY"] === "Y"?"LEFT":"INNER")." JOIN b_mail_filter MF ON MB.ID=MF.MAILBOX_ID ";
3169
3170 if(!is_array($arFilter))
3171 $arFilter = Array();
3172 $arSqlSearch = Array();
3173 $filter_keys = array_keys($arFilter);
3174
3175 for($i = 0, $n = count($filter_keys); $i < $n; $i++)
3176 {
3177 $val = $arFilter[$filter_keys[$i]];
3178 if ($val == '') continue;
3179 $key = mb_strtoupper($filter_keys[$i]);
3180 switch($key)
3181 {
3182 case "NAME":
3183 case "PHP_CONDITION":
3184 case "ACTION_PHP":
3185 $arSqlSearch[] = GetFilterQuery("MF.".$key, $val);
3186 break;
3187 case "SERVER_TYPE":
3188 $arSqlSearch[] = GetFilterQuery("MB.".$key, $val, "N");
3189 break;
3190 case "ID":
3191 case "ACTION_TYPE":
3192 case "MAILBOX_ID":
3193 case "PARENT_FILTER_ID":
3194 case "SORT":
3195 case "WHEN_MAIL_RECEIVED":
3196 case "WHEN_MANUALLY_RUN":
3197 case "ACTION_STOP_EXEC":
3198 case "ACTION_DELETE_MESSAGE":
3199 case "ACTION_READ":
3200 case "ACTIVE":
3201 $arSqlSearch[] = GetFilterQuery("MF.".$key, $val, "N");
3202 break;
3203 }
3204 }
3205
3206 $is_filtered = false;
3207 $strSqlSearch = "";
3208 for($i = 0, $n = count($arSqlSearch); $i < $n; $i++)
3209 {
3210 if($arSqlSearch[$i] <> '')
3211 {
3212 $strSqlSearch .= " AND (".$arSqlSearch[$i].") ";
3213 $is_filtered = true;
3214 }
3215 }
3216
3217 $arSqlOrder = Array();
3218 foreach($arOrder as $by=>$order)
3219 {
3220 $order = mb_strtolower($order);
3221 if ($order!="asc")
3222 $order = "desc".($DB->type == "ORACLE"?" NULLS LAST":"");
3223 else
3224 $order = "asc".($DB->type == "ORACLE"?" NULLS FIRST":"");
3225
3226 switch(mb_strtoupper($by))
3227 {
3228 case "TIMESTAMP_X":
3229 case "MAILBOX_ID":
3230 case "ACTIVE":
3231 case "NAME":
3232 case "SORT":
3233 case "PARENT_FILTER_ID":
3234 case "WHEN_MAIL_RECEIVED":
3235 case "WHEN_MANUALLY_RUN":
3236 case "ACTION_STOP_EXEC":
3237 case "ACTION_DELETE_MESSAGE":
3238 case "ACTION_READ":
3239 $arSqlOrder[] = " MF.".$by." ".$order." ";
3240 break;
3241 case "MAILBOX_NAME":
3242 $arSqlOrder[] = " MB.NAME ".$order." ";
3243 $arSqlOrder[] = " MF.ID ".$order." ";
3244 break;
3245 default:
3246 $arSqlOrder[] = " MF.ID ".$order." ";
3247 }
3248 }
3249
3250 $strSqlOrder = "";
3251 $arSqlOrder = array_unique($arSqlOrder);
3252 DelDuplicateSort($arSqlOrder);
3253
3254 for ($i = 0, $n = count($arSqlOrder); $i < $n; $i++)
3255 {
3256 if($i==0)
3257 $strSqlOrder = " ORDER BY ";
3258 else
3259 $strSqlOrder .= ",";
3260
3261 $strSqlOrder .= $arSqlOrder[$i];
3262 }
3263
3264 $strSql .= " WHERE 1=1 ".$strSqlSearch.$strSqlOrder;
3265
3266 $res = $DB->Query($strSql);
3267 $res->is_filtered = $is_filtered;
3268 return $res;
3269 }
3270
3271 public static function GetByID($ID)
3272 {
3273 global $DB;
3274 return CMailFilter::GetList(Array(), Array("ID"=>$ID));
3275 }
3276
3277 public static function CheckPHP($code, $field_name)
3278 {
3279 return true; // not work - E_CODE_ERROR
3280
3281 global $php_errormsg;
3282 ini_set("track_errors", "on");
3283 $php_errormsg_prev = $php_errormsg;
3284 ob_start();
3285 error_reporting(0);
3286 @eval($code);
3287 ob_end_clean();
3288 if($php_errormsg != "")
3289 CMailError::SetError("B_MAIL_ERR_PHP", GetMessage("MAIL_CL_ERR_IN_PHP").$field_name.". (".$php_errormsg.")");
3290 $php_errormsg = $php_errormsg_prev;
3291 ini_set("track_errors", $prev);
3292 }
3293
3294 public static function CheckConditionTypes($fields)
3295 {
3296 if(is_set($fields, 'CONDITIONS'))
3297 {
3298 $errors = [];
3299
3300 $whiteList = [
3301 'TYPE',
3302 'STRINGS',
3303 'COMPARE_TYPE',
3304 'ID',
3305 'FILTER_ID',
3306 ];
3307
3308 foreach ($fields['CONDITIONS'] as $item)
3309 {
3310 foreach ($item as $key => $value)
3311 {
3312 if(!in_array($key, $whiteList))
3313 {
3314 $errors[] = array("id"=>"INVALID_CONDITION_TYPE", "text"=> GetMessage("MAIL_INVALID_CONDITION_TYPE"));
3315 $GLOBALS["APPLICATION"]->ThrowException(new CAdminException($errors));
3316 return false;
3317 }
3318 }
3319 }
3320 }
3321 return true;
3322 }
3323
3324 public static function CheckFields($arFields, $ID=false)
3325 {
3326 $err_cnt = CMailError::ErrCount();
3327 $arMsg = Array();
3328
3329 if(is_set($arFields, "NAME") && mb_strlen($arFields["NAME"]) < 1)
3330 {
3331 CMailError::SetError("B_MAIL_ERR_NAME", GetMessage("MAIL_CL_ERR_NAME")." \"".GetMessage("MAIL_CL_NAME")."\"");
3332 $arMsg[] = array("id"=>"NAME", "text"=> GetMessage("MAIL_CL_ERR_NAME")." \"".GetMessage("MAIL_CL_NAME")."\"");
3333 }
3334
3335 if(is_set($arFields, "PHP_CONDITION") && trim($arFields["PHP_CONDITION"]) <> '')
3336 {
3337 if (!CMailFilter::CheckPHP($arFields["PHP_CONDITION"], GetMessage("MAIL_CL_PHP_COND")))
3338 $arMsg[] = array("id"=>"PHP_CONDITION", "text"=> GetMessage("MAIL_CL_ERR_IN_PHP").GetMessage("MAIL_CL_PHP_COND"));
3339 }
3340
3341 if(is_set($arFields, "ACTION_PHP") && trim($arFields["ACTION_PHP"]) <> '')
3342 {
3343 if (!CMailFilter::CheckPHP($arFields["ACTION_PHP"], GetMessage("MAIL_CL_PHP_ACT")))
3344 $arMsg[] = array("id"=>"ACTION_PHP", "text"=> GetMessage("MAIL_CL_ERR_IN_PHP").GetMessage("MAIL_CL_PHP_ACT"));
3345 }
3346
3347 if(is_set($arFields, "MAILBOX_ID"))
3348 {
3349 $r = CMailBox::GetByID($arFields["MAILBOX_ID"]);
3350 if(!$r->Fetch())
3351 {
3352 CMailError::SetError("B_MAIL_ERR_BAD_MAILBOX", GetMessage("MAIL_CL_ERR_WRONG_MAILBOX"));
3353 $arMsg[] = array("id"=>"MAILBOX_ID", "text"=> GetMessage("MAIL_CL_ERR_WRONG_MAILBOX"));
3354 }
3355 }
3356 elseif($ID===false)
3357 {
3358 CMailError::SetError("B_MAIL_ERR_BAD_MAILBOX_NA", GetMessage("MAIL_CL_ERR_MAILBOX_NA"));
3359 $arMsg[] = array("id"=>"MAILBOX_ID", "text"=> GetMessage("MAIL_CL_ERR_MAILBOX_NA"));
3360 }
3361
3362 if(!empty($arMsg))
3363 {
3364 $e = new CAdminException($arMsg);
3365 $GLOBALS["APPLICATION"]->ThrowException($e);
3366 return false;
3367 }
3368 return true;
3369
3370 //return ($err_cnt == CMailError::ErrCount());
3371 }
3372
3373 public static function Add($arFields)
3374 {
3376 {
3377 return false;
3378 }
3379
3380 global $DB;
3381
3382 if(is_set($arFields, "ACTIVE") && $arFields["ACTIVE"]!="Y")
3383 $arFields["ACTIVE"]="N";
3384
3385 if(is_set($arFields, "ACTION_READ") && $arFields["ACTION_READ"]!="Y" && $arFields["ACTION_READ"]!="N")
3386 $arFields["ACTION_READ"] = "-";
3387
3388 if(is_set($arFields, "ACTION_SPAM") && $arFields["ACTION_SPAM"]!="Y" && $arFields["ACTION_SPAM"]!="N")
3389 $arFields["ACTION_SPAM"] = "-";
3390
3391 if(is_set($arFields, "ACTION_DELETE_MESSAGE") && $arFields["ACTION_DELETE_MESSAGE"]!="Y")
3392 $arFields["ACTION_DELETE_MESSAGE"] ="N";
3393
3394 if(is_set($arFields, "ACTION_STOP_EXEC") && $arFields["ACTION_STOP_EXEC"]!="Y")
3395 $arFields["ACTION_STOP_EXEC"] = "N";
3396
3398 return false;
3399
3400 $ID = $DB->Add("b_mail_filter", $arFields, Array("PHP_CONDITION", "ACTION_PHP"));
3401
3402 if(is_set($arFields, "CONDITIONS"))
3404
3406
3407 return $ID;
3408 }
3409
3410
3411 public static function Update($ID, $arFields)
3412 {
3414 {
3415 return false;
3416 }
3417
3418 global $DB;
3419 $ID = intval($ID);
3420
3421 if(is_set($arFields, "ACTIVE") && $arFields["ACTIVE"]!="Y")
3422 $arFields["ACTIVE"]="N";
3423 if(is_set($arFields, "WHEN_MAIL_RECEIVED") && $arFields["WHEN_MAIL_RECEIVED"]!="Y")
3424 $arFields["WHEN_MAIL_RECEIVED"] = "N";
3425 if(is_set($arFields, "WHEN_MANUALLY_RUN") && $arFields["WHEN_MANUALLY_RUN"]!="Y")
3426 $arFields["WHEN_MANUALLY_RUN"] = "N";
3427 if(is_set($arFields, "ACTION_READ") && $arFields["ACTION_READ"]!="Y" && $arFields["ACTION_READ"]!="N")
3428 $arFields["ACTION_READ"] = "-";
3429 if(is_set($arFields, "ACTION_SPAM") && $arFields["ACTION_SPAM"]!="Y" && $arFields["ACTION_SPAM"]!="N")
3430 $arFields["ACTION_SPAM"] = "-";
3431 if(is_set($arFields, "ACTION_DELETE_MESSAGE") && $arFields["ACTION_DELETE_MESSAGE"]!="Y")
3432 $arFields["ACTION_DELETE_MESSAGE"] ="N";
3433 if(is_set($arFields, "ACTION_STOP_EXEC") && $arFields["ACTION_STOP_EXEC"]!="Y")
3434 $arFields["ACTION_STOP_EXEC"] = "N";
3435
3437 return false;
3438
3439 $arUpdateBinds = array();
3440 $strUpdate = $DB->PrepareUpdateBind("b_mail_filter", $arFields,"", false, $arUpdateBinds);
3441
3442 $strSql =
3443 "UPDATE b_mail_filter SET ".
3444 $strUpdate." ".
3445 "WHERE ID=".$ID;
3446
3447 $arBinds = array();
3448 foreach($arUpdateBinds as $field_id)
3449 $arBinds[$field_id] = $arFields[$field_id];
3450
3451 $DB->QueryBind($strSql, $arBinds);
3452
3453 if(is_set($arFields, "CONDITIONS"))
3455
3457
3458 return true;
3459 }
3460
3461 public static function Delete($ID)
3462 {
3463 global $DB;
3464 $ID = intval($ID);
3465 $dbr = CMailFilterCondition::GetList(Array(), Array("FILTER_ID"=>$ID));
3466 while($r = $dbr->Fetch())
3467 {
3468 if(!CMailFilterCondition::Delete($r["ID"]))
3469 return false;
3470 }
3471
3472 $strSql = "DELETE FROM b_mail_filter WHERE ID=".$ID;
3474 return $DB->Query($strSql, true);
3475 }
3476
3477 public static function Filter($arFields, $event, $FILTER_ID=false, $PARENT_FILTER_ID = false)
3478 {
3479 global $BX_MAIL_FILTER_CACHE, $DB;
3480 $PARENT_FILTER_ID = intval($PARENT_FILTER_ID);
3481 $MAILBOX_ID = intval($arFields["MAILBOX_ID"]);
3482 $MESSAGE_ID = intval($arFields["ID"]);
3483
3484 $cache_param = $MAILBOX_ID."|".$PARENT_FILTER_ID."|".$event."|".$FILTER_ID;
3485
3486 if(is_set($BX_MAIL_FILTER_CACHE, $cache_param))
3487 {
3488 $arFilterCond = $BX_MAIL_FILTER_CACHE[$cache_param]["CONDITIONS"];
3489 $arFilter = $BX_MAIL_FILTER_CACHE[$cache_param]["FILTER"];
3490 }
3491 else
3492 {
3493 $strSqlAdd = "";
3494 if($event=="R")
3495 $strSqlAdd .= " AND (WHEN_MAIL_RECEIVED='Y')";
3496 else
3497 $strSqlAdd .= " AND (WHEN_MANUALLY_RUN='Y' ".(intval($FILTER_ID)>0?" AND f.ID='".intval($FILTER_ID)."'":"").")";
3498
3499 $strSql =
3500 "SELECT f.*, c.*, f.ID, c.ID as CONDITION_ID
3501 FROM b_mail_filter f LEFT JOIN b_mail_filter_cond c ON f.ID = c.FILTER_ID
3502 WHERE (f.MAILBOX_ID = ".$MAILBOX_ID." OR MAILBOX_ID IS NULL)
3503 AND f.ACTIVE = 'Y'
3504 AND (f.PARENT_FILTER_ID = " . ($PARENT_FILTER_ID > 0 ? $PARENT_FILTER_ID : "0 OR f.PARENT_FILTER_ID IS NULL") . ")" .
3505 $strSqlAdd."
3506 ORDER BY f.SORT, f.ID";
3507
3508 $dbr = $DB->Query($strSql);
3509
3510 $arFilter = Array();
3511 $arFilterCond = Array();
3512 $prev_ID = 0;
3513 $arr_prev = false;
3514 $arConds = Array();
3515 while($arr = $dbr->Fetch())
3516 {
3517 $arFilter[$arr["ID"]] = $arr;
3518 if($arr["CONDITION_ID"]>0)
3519 {
3520 if(!is_array($arFilterCond[$arr["ID"]]))
3521 $arFilterCond[$arr["ID"]] = Array();
3522 $arFilterCond[$arr["ID"]][] = $arr;
3523 }
3524 }
3525
3526 $BX_MAIL_FILTER_CACHE[$cache_param] = Array("FILTER"=>$arFilter, "CONDITIONS"=>$arFilterCond);
3527 }
3528
3529 $arFieldsOriginal = $arFields;
3530 foreach($arFilter as $filter_id=>$arFilterParams)
3531 {
3532 $arFields = $arFieldsOriginal;
3533 $arFields["MAIL_FILTER"] = $arFilterParams;
3534
3535 $arAllConditions = isset($arFilterCond[$filter_id]) ? $arFilterCond[$filter_id] : [];
3536 $bCondOK = true;
3537 if(!is_array($arAllConditions))
3538 $arAllConditions = Array();
3539 foreach($arAllConditions as $k => $arCondition)
3540 {
3541 $bCondOK = false;
3542 $type = $arCondition["TYPE"];
3543 switch($type)
3544 {
3545 case "ALL":case "RECIPIENT":case "SENDER":
3546 if($type=="ALL")
3547 $arFields[$type] = $arFields["HEADER"]."\r\n".$arFields["BODY"];
3548 elseif($type=="RECIPIENT")
3549 $arFields[$type] = $arFields["FIELD_CC"]."\r\n".$arFields["FIELD_TO"]."\r\n".$arFields["FIELD_BCC"];
3550 else
3551 $arFields[$type] = $arFields["FIELD_FROM"]."\r\n".$arFields["FIELD_REPLY_TO"];
3552 case "HEADER": case "FIELD_FROM": case "FIELD_REPLY_TO": case "FIELD_TO": case "FIELD_CC": case "SUBJECT": case "BODY":
3553 $arStrings = explode("\n", $arCondition["STRINGS"]);
3554 if($arCondition["COMPARE_TYPE"]=="NOT_EQUAL" || $arCondition["COMPARE_TYPE"]=="NOT_CONTAIN")
3555 {
3556 $bCondOK = true;
3557 for($i = 0, $n = count($arStrings); $i < $n; $i++)
3558 {
3559 $str = mb_strtoupper(Trim($arStrings[$i], "\r"));
3560 switch($arCondition["COMPARE_TYPE"])
3561 {
3562 case "NOT_CONTAIN":
3563 if($str <> '' && mb_strpos(mb_strtoupper($arFields[$type]), $str) !== false)
3564 $bCondOK = false;
3565 break;
3566 case "NOT_EQUAL":
3567 if($str == mb_strtoupper($arFields[$type]))
3568 $bCondOK = false;
3569 break;
3570 }
3571
3572 if(!$bCondOK)
3573 break;
3574 }
3575 }
3576 else
3577 {
3578 for($i = 0, $n = count($arStrings); $i < $n; $i++)
3579 {
3580 $str = mb_strtoupper(Trim($arStrings[$i], "\r"));
3581 switch($arCondition["COMPARE_TYPE"])
3582 {
3583 case "CONTAIN":
3584 if($str <> '' && mb_strpos(mb_strtoupper($arFields[$type]), $str) !== false)
3585 $bCondOK = true;
3586 break;
3587 case "EQUAL":
3588 if($str == mb_strtoupper($arFields[$type]))
3589 $bCondOK = true;
3590 break;
3591 case "REGEXP":
3592 if(preg_match("'".str_replace("'", "\'", $str)."'i", $arFields[$type]))
3593 $bCondOK = true;
3594 break;
3595 }
3596
3597 if($bCondOK)
3598 break;
3599 }
3600 }
3601 break;
3602
3603 case "ATTACHMENT":
3604 $db_att = CMailAttachment::GetList(Array(), Array("MESSAGE_ID"=>$arFields["ID"]));
3605 $arStrings = explode("\n", $arCondition["STRINGS"]);
3606 if($arCondition["COMPARE_TYPE"]=="NOT_EQUAL" || $arCondition["COMPARE_TYPE"]=="NOT_CONTAIN")
3607 {
3608 $bCondOK = true;
3609 while($arr_att = $db_att->Fetch())
3610 {
3611 for($i = 0, $n = count($arStrings); $i < $n; $i++)
3612 {
3613 $str = mb_strtoupper(Trim($arStrings[$i], "\r"));
3614 switch($arCondition["COMPARE_TYPE"])
3615 {
3616 case "NOT_CONTAIN":
3617 if($str <> '' && mb_strpos(mb_strtoupper($arr_att["FILE_NAME"]), $str) !== false)
3618 $bCondOK = false;
3619 break;
3620 case "NOT_EQUAL":
3621 if($str == mb_strtoupper($arr_att["FILE_NAME"]))
3622 $bCondOK = false;
3623 break;
3624 }
3625 }
3626 if(!$bCondOK)
3627 break;
3628 }
3629 }
3630 else
3631 {
3632 while($arr_att = $db_att->Fetch())
3633 {
3634 for($i = 0, $n = count($arStrings); $i < $n; $i++)
3635 {
3636 $str = mb_strtoupper(Trim($arStrings[$i], "\r"));
3637 switch($arCondition["COMPARE_TYPE"])
3638 {
3639 case "CONTAIN":
3640 if($str <> '' && mb_strpos(mb_strtoupper($arr_att["FILE_NAME"]), $str) !== false)
3641 $bCondOK = true;
3642 break;
3643 case "EQUAL":
3644 if($str == mb_strtoupper($arr_att["FILE_NAME"]))
3645 $bCondOK = true;
3646 break;
3647 case "REGEXP":
3648 if(preg_match("'".str_replace("'", "\'", $str)."'i", $arr_att["FILE_NAME"]))
3649 $bCondOK = true;
3650 break;
3651 }
3652 }
3653 if($bCondOK)
3654 break;
3655 }
3656 }
3657 break;
3658 } //switch
3659
3660 if(!$bCondOK)
3661 break;
3662 } //foreach($arAllConditions as $k => $arCondition)
3663
3664 if(!$bCondOK)
3665 continue;
3666
3667 if($arFilterParams["SPAM_RATING"]>0)
3668 {
3670 if($arFilterParams["SPAM_RATING_TYPE"]==">" && $arFields["SPAM_RATING"]<=$arFilterParams["SPAM_RATING"])
3671 continue;
3672 if($arFilterParams["SPAM_RATING_TYPE"]!=">" && $arFields["SPAM_RATING"]>=$arFilterParams["SPAM_RATING"])
3673 continue;
3674 }
3675
3676 if($arFilterParams["MESSAGE_SIZE"]>0)
3677 {
3678 $MESSAGE_SIZE = $arFields["MESSAGE_SIZE"];
3679 if($arFilterParams["MESSAGE_SIZE_UNIT"]=="k")
3680 $MESSAGE_SIZE = intval($MESSAGE_SIZE/1024);
3681 elseif($arFilterParams["MESSAGE_SIZE_UNIT"]=="m")
3682 $MESSAGE_SIZE = intval($MESSAGE_SIZE/1024/1024);
3683
3684 if($arFilterParams["MESSAGE_SIZE_TYPE"]==">" && $MESSAGE_SIZE<=$arFilterParams["MESSAGE_SIZE"])
3685 continue;
3686 if($arFilterParams["MESSAGE_SIZE_TYPE"]!=">" && $MESSAGE_SIZE>=$arFilterParams["MESSAGE_SIZE"])
3687 continue;
3688 }
3689
3690 if($arFilterParams["PHP_CONDITION"] <> '')
3691 if(!CMailFilter::DoPHPAction("php_cond_".$arFilterParams["ID"]."_", $arFilterParams["PHP_CONDITION"], $arFields))
3692 continue;
3693
3694 $arModFilter = false;
3695 if($arFilterParams["ACTION_TYPE"]!="")
3696 {
3697 $res = CMailFilter::GetFilterList($arFilterParams["ACTION_TYPE"]);
3698 if($arModFilter = $res->Fetch())
3699 {
3700 if (
3701 (is_array($arModFilter["CONDITION_FUNC"]) && count($arModFilter["CONDITION_FUNC"]) > 0) ||
3702 $arModFilter["CONDITION_FUNC"] <> ''
3703 )
3704 if(!call_user_func_array($arModFilter["CONDITION_FUNC"], Array(&$arFields, &$arFilterParams["ACTION_VARS"])))
3705 continue;
3706 }
3707 }
3709 Array(
3710 "MAILBOX_ID"=>$MAILBOX_ID,
3711 "MESSAGE_ID"=>$MESSAGE_ID,
3712 "FILTER_ID"=>$filter_id,
3713 "STATUS_GOOD"=>"Y",
3714 "LOG_TYPE"=>"FILTER_OK",
3715 "MESSAGE"=>$event,
3716 )
3717 );
3718
3719 if($arModFilter)
3720 if (
3721 (is_array($arModFilter["ACTION_FUNC"]) && count($arModFilter["ACTION_FUNC"]) > 0) ||
3722 $arModFilter["ACTION_FUNC"] <> ''
3723 )
3724 call_user_func_array($arModFilter["ACTION_FUNC"], array(&$arFields, &$arFilterParams["ACTION_VARS"]));
3725
3726
3727 if(Trim($arFilterParams["ACTION_PHP"]) <> '')
3728 {
3729 $res = CMailFilter::DoPHPAction("php_act_".$arFilterParams["ID"]."_", $arFilterParams["ACTION_PHP"], $arFields);
3731 Array(
3732 "MAILBOX_ID"=>$MAILBOX_ID,
3733 "MESSAGE_ID"=>$MESSAGE_ID,
3734 "FILTER_ID"=>$filter_id,
3735 "LOG_TYPE"=>"DO_PHP",
3736 "MESSAGE"=>""
3737 )
3738 );
3739 }
3740
3741 if($arFilterParams["ACTION_SPAM"]=="Y" && $arFields["SPAM"]!="Y")
3742 {
3743 if($arFields["SPAM"]=="N")
3744 CMailFilter::DeleteFromSpamBase($arFields["FOR_SPAM_TEST"], false);
3745 CMailFilter::MarkAsSpam($arFields["FOR_SPAM_TEST"], true);
3746 CMailMessage::Update($MESSAGE_ID, Array("SPAM"=>"Y"));
3748 Array(
3749 "MAILBOX_ID"=>$MAILBOX_ID,
3750 "MESSAGE_ID"=>$MESSAGE_ID,
3751 "FILTER_ID"=>$filter_id,
3752 "LOG_TYPE"=>"SPAM",
3753 "MESSAGE"=>""
3754 )
3755 );
3756 $arFields["SPAM"] = "Y";
3757 }
3758 elseif($arFilterParams["ACTION_SPAM"]=="N" && $arFields["SPAM"]!="N")
3759 {
3760 if($arFields["SPAM"]=="Y")
3761 CMailFilter::DeleteFromSpamBase($arFields["FOR_SPAM_TEST"], true);
3762 CMailFilter::MarkAsSpam($arFields["FOR_SPAM_TEST"], false);
3763 CMailMessage::Update($MESSAGE_ID, Array("SPAM"=>"N"));
3765 Array(
3766 "MAILBOX_ID"=>$MAILBOX_ID,
3767 "MESSAGE_ID"=>$MESSAGE_ID,
3768 "FILTER_ID"=>$filter_id,
3769 "LOG_TYPE"=>"NOTSPAM",
3770 "MESSAGE"=>""
3771 )
3772 );
3773 $arFields["SPAM"] = "N";
3774 }
3775
3776 if($arFilterParams["ACTION_READ"]=="Y" && $arFields["NEW_MESSAGE"]=="Y")
3777 {
3778 $arFields["NEW_MESSAGE"] = "N";
3779 CMailMessage::Update($MESSAGE_ID, Array("NEW_MESSAGE"=>"N"));
3780 }
3781 elseif($arFilterParams["ACTION_READ"]=="N" && $arFields["NEW_MESSAGE"]!="Y")
3782 {
3783 $arFields["NEW_MESSAGE"] = "Y";
3784 CMailMessage::Update($MESSAGE_ID, Array("NEW_MESSAGE"=>"Y"));
3785 }
3786
3787 if($arFilterParams["ACTION_DELETE_MESSAGE"]=="Y")
3788 {
3790 Array(
3791 "MAILBOX_ID"=>$MAILBOX_ID,
3792 "MESSAGE_ID"=>$MESSAGE_ID,
3793 "FILTER_ID"=>$filter_id,
3794 "STATUS_GOOD"=>"Y",
3795 "LOG_TYPE"=>"MESSAGE_DELETED",
3796 "MESSAGE"=>""
3797 )
3798 );
3799 CMailMessage::Delete($MESSAGE_ID);
3800 }
3801
3802 if($arFilterParams["ACTION_STOP_EXEC"]=="Y")
3803 {
3805 Array(
3806 "MAILBOX_ID"=>$MAILBOX_ID,
3807 "MESSAGE_ID"=>$MESSAGE_ID,
3808 "FILTER_ID"=>$filter_id,
3809 "STATUS_GOOD"=>"Y",
3810 "LOG_TYPE"=>"FILTER_STOP",
3811 "MESSAGE"=>""
3812 )
3813 );
3814 return true;
3815 }
3816 }
3817
3818 return true;
3819 }
3820
3821
3822 public static function FilterMessage($message_id, $event, $FILTER_ID=false)
3823 {
3824 $res = CMailMessage::GetByID($message_id);
3825 if($arFields = $res->Fetch())
3826 return CMailFilter::Filter($arFields, $event, $FILTER_ID);
3827
3828 return false;
3829 }
3830
3831 public static function RecalcSpamRating()
3832 {
3833 global $DB;
3834 $res = $DB->Query("SELECT ID, HEADER, BODY_HTML, BODY FROM b_mail_message WHERE SPAM_LAST_RESULT<>'N'");
3835 while($arr = $res->Fetch())
3836 {
3837 $forSpamTest = sprintf('%s %s', $arr['HEADER'], $arr['BODY_HTML'] ?: $arr['BODY']);
3838 $arSpam = CMailFilter::GetSpamRating($forSpamTest);
3839 $DB->Query("UPDATE b_mail_message SET SPAM_RATING=".Round($arSpam["RATING"], 4).", SPAM_LAST_RESULT='Y', SPAM_WORDS='".$DB->ForSql($arSpam["WORDS"], 255)."' WHERE ID=".$arr["ID"]);
3840 }
3841 }
3842
3843 public static function GetSpamRating($message)
3844 {
3845 global $DB;
3846
3847 $arWords = CMailFilter::getWords($message, 1000);
3848
3849 if (empty($arWords))
3850 return 0;
3851
3852 // for every word find Si
3853 $arWords = array_map("md5", $arWords);
3854
3855 global $BX_MAIL_SPAM_CNT;
3856 if(!is_set($BX_MAIL_SPAM_CNT, "G"))
3857 {
3858 $strSql = "SELECT MAX(GOOD_CNT) as G, MAX(BAD_CNT) as B FROM b_mail_spam_weight";
3859 if($res = $DB->Query($strSql))
3860 $BX_MAIL_SPAM_CNT = $res->Fetch();
3861
3862 if(intval($BX_MAIL_SPAM_CNT["G"])<=0)
3863 $BX_MAIL_SPAM_CNT["G"] = 1;
3864
3865 if(intval($BX_MAIL_SPAM_CNT["B"])<=0)
3866 $BX_MAIL_SPAM_CNT["B"] = 1;
3867 }
3868
3869 $CNT_WORDS = COption::GetOptionInt("mail", "spam_word_count", B_MAIL_WORD_CNT);
3870 $MIN_COUNT = COption::GetOptionInt("mail", "spam_min_count", B_MAIL_MIN_CNT);
3871 // select $CNT_WORDS words with max |Si - 0.5|
3872 // if the word placed less then xxx (5) times, then ignore
3873 $strSql =
3874 "SELECT SW.*, ".
3875 " (BAD_CNT/".$BX_MAIL_SPAM_CNT["B"].".0) / (2*GOOD_CNT/".$BX_MAIL_SPAM_CNT["G"].".0 + BAD_CNT/".$BX_MAIL_SPAM_CNT["B"].".0) as RATING, ".
3876 " ABS((BAD_CNT/".$BX_MAIL_SPAM_CNT["B"].".0) / (2*GOOD_CNT/".$BX_MAIL_SPAM_CNT["G"].".0 + BAD_CNT/".$BX_MAIL_SPAM_CNT["B"].".0) - 0.5) as MOD_RATING ".
3877 "FROM b_mail_spam_weight SW ".
3878 "WHERE WORD_ID IN ('".implode("', '", $arWords)."') ".
3879 " AND ABS((BAD_CNT/".$BX_MAIL_SPAM_CNT["B"].".0) / (2*GOOD_CNT/".$BX_MAIL_SPAM_CNT["G"].".0 + BAD_CNT/".$BX_MAIL_SPAM_CNT["B"].".0) - 0.5) > 0.1 ".
3880 " AND TOTAL_CNT>".$MIN_COUNT." ".
3881 "ORDER BY MOD_RATING DESC ".
3882 ($DB->type == "MYSQL"?"LIMIT ".$CNT_WORDS : "");
3883
3884 //echo htmlspecialcharsbx($strSql)."<br>";
3885
3886 $a = 1;
3887 $b = 1;
3888 $dbr = $DB->Query($strSql);
3889 $arr = true;
3890 $words = "";
3891
3892 for($i=0; $i<$CNT_WORDS; $i++)
3893 {
3894 if($arr && $arr = $dbr->Fetch())
3895 {
3896 //echo "<font size='-3'>".htmlspecialcharsbx($arr["WORD_REAL"])."=".$arr["RATING"]."<br></font> ";
3897 $words .= $arr["WORD_REAL"]." ".Round($arr["RATING"]*100, 4)." ".$arr["BAD_CNT"]." ".$arr["GOOD_CNT"]."\n";
3898 $a = $a * ($arr["RATING"]==0?0.00001:$arr["RATING"]);
3899 $b = $b * (1 - ($arr["RATING"]==1?0.9999:$arr["RATING"]));
3900 }
3901 else
3902 {
3903 //if there is no word then weight Si = 0.4
3904 $a = $a * 0.4;
3905 $b = $b * (1 - 0.4);
3906 }
3907 }
3908 // calculate Bayes for the whole message
3909 $rating = $a/($a+$b) * 100;
3910
3911 return Array("RATING"=>$rating, "WORDS"=>$words);
3912 }
3913
3914 public static function getWords($message, $max_words)
3915 {
3916 static $tok = null;
3917 if (!isset($tok))
3918 {
3919 $tok = "}{~";
3920 for($i = ord("\x01"); $i < ord("\x23"); $i++)
3921 $tok .= chr($i);
3922 for($i = ord("\x25"); $i < ord("\x3F"); $i++)
3923 $tok .= chr($i);
3924 for($i = ord("\x5B"); $i < ord("\x5E"); $i++)
3925 $tok .= chr($i);
3926 }
3927
3928 $arWords = array();
3929 $word = strtok($message, $tok);
3930 while($word !== false)
3931 {
3932 $arWords[$word] = $word;
3933 if (count($arWords) >= $max_words)
3934 break;
3935 $word = strtok($tok);
3936 }
3937 return $arWords;
3938 }
3939
3940 public static function DoPHPAction($id, $action, &$arMessageFields)
3941 {
3942 return eval($action);
3943 }
3944
3945 public static function DeleteFromSpamBase($message, $bIsSPAM = true)
3946 {
3947 CMailFilter::SpamAction($message, $bIsSPAM, true);
3948 }
3949
3950 public static function MarkAsSpam($message, $bIsSPAM = true)
3951 {
3953 }
3954
3955 public static function SpamAction($message, $bIsSPAM, $bDelete = false)
3956 {
3957 global $DB;
3958 global $BX_MAIL_SPAM_CNT;
3959
3960 if(!is_set($BX_MAIL_SPAM_CNT, "G"))
3961 {
3962 $strSql = "SELECT MAX(GOOD_CNT) as G, MAX(BAD_CNT) as B FROM b_mail_spam_weight";
3963 if($res = $DB->Query($strSql))
3964 $BX_MAIL_SPAM_CNT = $res->Fetch();
3965
3966 if(intval($BX_MAIL_SPAM_CNT["G"])<=0)
3967 $BX_MAIL_SPAM_CNT["G"] = 1;
3968
3969 if(intval($BX_MAIL_SPAM_CNT["B"])<=0)
3970 $BX_MAIL_SPAM_CNT["B"] = 1;
3971 }
3972
3973 if($bDelete && $bIsSPAM)
3974 $BX_MAIL_SPAM_CNT["B"]--;
3975 elseif($bDelete && !$bIsSPAM)
3976 $BX_MAIL_SPAM_CNT["G"]--;
3977 elseif(!$bDelete && $bIsSPAM)
3978 $BX_MAIL_SPAM_CNT["B"]++;
3979 elseif(!$bDelete && !$bIsSPAM)
3980 $BX_MAIL_SPAM_CNT["G"]++;
3981
3982 @set_time_limit(30);
3983
3984 // split to words
3985 $arWords = CMailFilter::getWords($message, 1000);
3986
3987 // for every word find Si
3988 $strWords = "''";
3989 foreach($arWords as $word)
3990 {
3991 $word_md5 = md5($word);
3992
3993 // change weight
3994 $strSql =
3995 "INSERT INTO b_mail_spam_weight(WORD_ID, WORD_REAL, GOOD_CNT, BAD_CNT, TOTAL_CNT) ".
3996 "VALUES('".$word_md5."', '".$DB->ForSql($word, 40)."', ".($bIsSPAM?0:1).", ".($bIsSPAM?1:0).", 1)";
3997
3998 if($bDelete || (!$DB->Query($strSql, true)))
3999 {
4000 if($bDelete)
4001 {
4002 $strSql =
4003 "UPDATE b_mail_spam_weight SET ".
4004 " GOOD_CNT = GOOD_CNT - ".($bIsSPAM?0:1).", ".
4005 " BAD_CNT = BAD_CNT - ".($bIsSPAM?1:0).", ".
4006 " TOTAL_CNT = TOTAL_CNT - 1 ".
4007 "WHERE WORD_ID = '".$word_md5."' ".
4008 " AND ".($bIsSPAM?"BAD_CNT>0":"GOOD_CNT>0");// AND WORD_REAL = '".$DB->ForSql($word, 40)."'";
4009 }
4010 else
4011 {
4012 $strSql =
4013 "UPDATE b_mail_spam_weight SET ".
4014 " GOOD_CNT = GOOD_CNT + ".($bIsSPAM?0:1).", ".
4015 " BAD_CNT = BAD_CNT + ".($bIsSPAM?1:0).", ".
4016 " TOTAL_CNT = TOTAL_CNT + 1 ".
4017 "WHERE WORD_ID='".$word_md5."'";// AND WORD_REAL = '".$DB->ForSql($word, 40)."'";
4018 }
4019
4020 $DB->Query($strSql);
4021 }
4022 }
4023
4024
4025 if(COption::GetOptionString("mail", "reset_all_spam_result", "N") == "Y")
4026 $DB->Query("UPDATE b_mail_message SET SPAM_LAST_RESULT='N'");
4027 }
4028
4029
4030 public static function GetFilterList($id = "")
4031 {
4032 static $BX_MAIL_CUST_FILTER_LIST = false;
4033 if($BX_MAIL_CUST_FILTER_LIST === false)
4034 {
4035 $BX_MAIL_CUST_FILTER_LIST = array();
4036 foreach(GetModuleEvents("mail", "OnGetFilterList", true) as $arEvent)
4037 {
4038 $arResult = ExecuteModuleEventEx($arEvent);
4039 if(is_array($arResult))
4040 $BX_MAIL_CUST_FILTER_LIST[] = $arResult;
4041 }
4042 }
4043
4044 if($id != "")
4045 {
4046 $allResultsTemp = array();
4047 foreach($BX_MAIL_CUST_FILTER_LIST as $arResult)
4048 {
4049 if($arResult["ID"] == $id)
4050 {
4051 $allResultsTemp[] = $arResult;
4052 break;
4053 }
4054 }
4055 }
4056 else
4057 {
4058 $allResultsTemp = $BX_MAIL_CUST_FILTER_LIST;
4059 }
4060
4061 $db_res = new CDBResult;
4062 $db_res->InitFromArray($allResultsTemp);
4063 return $db_res;
4064 }
4065}
4066
4068{
4069 public static function GetList($arOrder=Array(), $arFilter=Array())
4070 {
4071 global $DB;
4072 $strSql =
4073 "SELECT MFC.* ".
4074 "FROM b_mail_filter_cond MFC ";
4075
4076 if(!is_array($arFilter))
4077 $arFilter = Array();
4078 $arSqlSearch = Array();
4079 $filter_keys = array_keys($arFilter);
4080 for($i = 0, $n = count($filter_keys); $i < $n; $i++)
4081 {
4082 $val = $arFilter[$filter_keys[$i]];
4083 if ($val == '') continue;
4084 $key = mb_strtoupper($filter_keys[$i]);
4085 switch($key)
4086 {
4087 case "TYPE":
4088 case "STRINGS":
4089 case "COMPARE_TYPE":
4090 $arSqlSearch[] = GetFilterQuery("MFC.".$key, $val);
4091 break;
4092 case "ID":
4093 case "FILTER_ID":
4094 $arSqlSearch[] = GetFilterQuery("MFC.".$key, $val, "N");
4095 break;
4096 }
4097 }
4098
4099 $strSqlSearch = "";
4100 for($i = 0, $n = count($arSqlSearch); $i < $n; $i++)
4101 {
4102 if($arSqlSearch[$i] <> '')
4103 $strSqlSearch .= " AND (".$arSqlSearch[$i].") ";
4104 }
4105
4106 $arSqlOrder = Array();
4107 foreach($arOrder as $by=>$order)
4108 {
4109 $order = mb_strtolower($order);
4110 if ($order!="asc")
4111 $order = "desc".($DB->type == "ORACLE"?" NULLS LAST":"");
4112 else
4113 $order = "asc".($DB->type == "ORACLE"?" NULLS FIRST":"");
4114
4115 switch(mb_strtoupper($by))
4116 {
4117 case "FILTER_ID":
4118 case "TYPE":
4119 case "STRINGS":
4120 case "COMPARE_TYPE":
4121 $arSqlOrder[] = " MFC.".$by." ".$order." ";
4122 break;
4123 default:
4124 $arSqlOrder[] = " MFC.ID ".$order." ";
4125 }
4126 }
4127
4128 $strSqlOrder = "";
4129 $arSqlOrder = array_unique($arSqlOrder);
4130 DelDuplicateSort($arSqlOrder);
4131
4132 for ($i = 0, $n = count($arSqlOrder); $i < $n; $i++)
4133 {
4134 if($i==0)
4135 $strSqlOrder = " ORDER BY ";
4136 else
4137 $strSqlOrder .= ",";
4138
4139 $strSqlOrder .= $arSqlOrder[$i];
4140 }
4141
4142 $strSql .= " WHERE 1=1 ".$strSqlSearch.$strSqlOrder;
4143
4144 $res = $DB->Query($strSql);
4145 $res->is_filtered = (count($arSqlOrder)>0);
4146 return $res;
4147 }
4148
4149 function GetByID($ID)
4150 {
4151 global $DB;
4152 return CMailFilterCondition::GetList(Array(), Array("ID"=>$ID));
4153 }
4154
4155 public static function Delete($ID)
4156 {
4157 global $DB;
4158 $ID = intval($ID);
4159 $strSql = "DELETE FROM b_mail_filter_cond WHERE ID=".$ID;
4160 return $DB->Query($strSql, true);
4161 }
4162
4163 public static function SetConditions($FILTER_ID, $CONDITIONS, $bClearOther = true)
4164 {
4165 global $DB;
4166
4167 $FILTER_ID = intval($FILTER_ID);
4168
4169 $strSql=
4170 "SELECT ID ".
4171 "FROM b_mail_filter_cond ".
4172 "WHERE FILTER_ID=".$FILTER_ID;
4173
4174 $dbr = $DB->Query($strSql);
4175
4176 while($dbr_arr = $dbr->Fetch())
4177 {
4178 if(is_set($CONDITIONS, $dbr_arr["ID"]) && is_array($CONDITIONS[$dbr_arr["ID"]]) && $CONDITIONS[$dbr_arr["ID"]]["STRINGS"] <> '')
4179 {
4180 $arFields = $CONDITIONS[$dbr_arr["ID"]];
4181 unset($arFields["ID"]);
4182 $arFields["FILTER_ID"] = $FILTER_ID;
4183 CMailFilterCondition::Update($dbr_arr["ID"], $arFields);
4184 unset($CONDITIONS[$dbr_arr["ID"]]);
4185 }
4186 elseif($bClearOther)
4187 {
4188 $DB->Query("DELETE FROM b_mail_filter_cond WHERE ID=".$dbr_arr["ID"]);
4189 }
4190 }
4191
4192 foreach($CONDITIONS as $arFields)
4193 {
4194 if(is_array($arFields) && $arFields["STRINGS"] <> '')
4195 {
4196 $arFields["FILTER_ID"] = $FILTER_ID;
4197 unset($arFields["ID"]);
4199 }
4200 }
4201 }
4202
4203 public static function Add($arFields)
4204 {
4206 {
4207 return false;
4208 }
4209
4210 global $DB;
4211
4212 if(is_set($arFields, "COMPARE_TYPE") && $arFields["COMPARE_TYPE"]!="EQUAL" && $arFields["COMPARE_TYPE"]!="NOT_EQUAL" && $arFields["COMPARE_TYPE"]!="NOT_CONTAIN" && $arFields["COMPARE_TYPE"]!="REGEXP")
4213 $arFields["COMPARE_TYPE"]="CONTAIN";
4214
4215 $ID = $DB->Add("b_mail_filter_cond", $arFields);
4216 return $ID;
4217 }
4218
4219
4220 public static function Update($ID, $arFields)
4221 {
4223 {
4224 return false;
4225 }
4226
4227 global $DB;
4228 $ID = intval($ID);
4229
4230 if(is_set($arFields, "COMPARE_TYPE") && $arFields["COMPARE_TYPE"]!="EQUAL" && $arFields["COMPARE_TYPE"]!="NOT_EQUAL" && $arFields["COMPARE_TYPE"]!="NOT_CONTAIN" && $arFields["COMPARE_TYPE"]!="REGEXP")
4231 $arFields["COMPARE_TYPE"]="CONTAIN";
4232
4233
4234 $strUpdate = $DB->PrepareUpdate("b_mail_filter_cond", $arFields);
4235
4236 $strSql =
4237 "UPDATE b_mail_filter_cond SET ".
4238 $strUpdate." ".
4239 "WHERE ID=".$ID;
4240
4241 $DB->Query($strSql);
4242
4243 return true;
4244 }
4245}
4246
4247
4249{
4250 public static function AddMessage($arFields)
4251 {
4252 global $DB;
4253
4254 if (COption::getOptionString('mail', 'disable_log', 'N') == 'Y')
4255 return;
4256
4257 $arFields["~DATE_INSERT"] = $DB->GetNowFunction();
4258 if(array_key_exists('MESSAGE', $arFields))
4259 $arFields['MESSAGE'] = strval(mb_substr($arFields['MESSAGE'], 0, 255));
4260 else
4261 $arFields['MESSAGE'] = '';
4262
4263 return $DB->Add("b_mail_log", $arFields);
4264 }
4265
4266 public static function Delete($ID)
4267 {
4268 global $DB;
4269 $ID = intval($ID);
4270 $strSql = "DELETE FROM b_mail_log WHERE ID=".$ID;
4271 return $DB->Query($strSql, true);
4272 }
4273
4274 public static function GetList($arOrder=Array(), $arFilter=Array())
4275 {
4276 global $DB;
4277 $strSql =
4278 "SELECT ML.*, MB.NAME as MAILBOX_NAME, ".
4279 " MF.NAME as FILTER_NAME, ".
4280 " MM.SUBJECT as MESSAGE_SUBJECT, ".
4281 " ".$DB->DateToCharFunction("ML.DATE_INSERT")." as DATE_INSERT ".
4282 " ".
4283 "FROM b_mail_log ML ".
4284 " INNER JOIN b_mail_mailbox MB ON MB.ID=ML.MAILBOX_ID ".
4285 " LEFT JOIN b_mail_filter MF ON MF.ID=ML.FILTER_ID ".
4286 " LEFT JOIN b_mail_message MM ON MM.ID=ML.MESSAGE_ID ";
4287
4288 if(!is_array($arFilter))
4289 $arFilter = Array();
4290 $arSqlSearch = Array();
4291 $filter_keys = array_keys($arFilter);
4292 for($i = 0, $n = count($filter_keys); $i < $n; $i++)
4293 {
4294 $val = $arFilter[$filter_keys[$i]];
4295 if ($val == '') continue;
4296 $key = mb_strtoupper($filter_keys[$i]);
4297 switch($key)
4298 {
4299 case "ID":
4300 case "MAILBOX_ID":
4301 case "FILTER_ID":
4302 case "MESSAGE_ID":
4303 case "LOG_TYPE":
4304 case "STATUS_GOOD":
4305 $arSqlSearch[] = GetFilterQuery("ML.".$key, $val, "N");
4306 break;
4307 case "MESSAGE":
4308 $arSqlSearch[] = GetFilterQuery("ML.".$key, $val);
4309 break;
4310 case "FILTER_NAME":
4311 $arSqlSearch[] = GetFilterQuery("MF.NAME", $val);
4312 break;
4313 case "MAILBOX_NAME":
4314 $arSqlSearch[] = GetFilterQuery("MB.NAME", $val);
4315 break;
4316 case "MESSAGE_SUBJECT":
4317 $arSqlSearch[] = GetFilterQuery("MM.SUBJECT", $val);
4318 break;
4319 }
4320 }
4321
4322 $is_filtered = false;
4323 $strSqlSearch = "";
4324 for($i = 0, $n = count($arSqlSearch); $i < $n; $i++)
4325 {
4326 if($arSqlSearch[$i] <> '')
4327 {
4328 $strSqlSearch .= " AND (".$arSqlSearch[$i].") ";
4329 $is_filtered = true;
4330 }
4331 }
4332
4333 $arSqlOrder = Array();
4334 foreach($arOrder as $by=>$order)
4335 {
4336 $order = mb_strtolower($order);
4337 if ($order!="asc")
4338 $order = "desc".($DB->type == "ORACLE"?" NULLS LAST":"");
4339 else
4340 $order = "asc".($DB->type == "ORACLE"?" NULLS FIRST":"");
4341
4342 switch(mb_strtoupper($by))
4343 {
4344 case "ID":
4345 case "MAILBOX_ID":
4346 case "FILTER_ID":
4347 case "MESSAGE_ID":
4348 case "DATE_INSERT":
4349 case "LOG_TYPE":
4350 case "STATUS_GOOD":
4351 case "MESSAGE":
4352 $arSqlOrder[] = " ML.".$by." ".$order." ";
4353 case "MESSAGE_SUBJECT":
4354 $arSqlOrder[] = " MM.SUBJECT ".$order." ";
4355 case "FILTER_NAME":
4356 $arSqlOrder[] = " MF.NAME ".$order." ";
4357 case "MAILBOX_NAME":
4358 $arSqlOrder[] = " MB.NAME ".$order." ";
4359 default:
4360 $arSqlOrder[] = " ML.ID ".$order." ";
4361 }
4362 }
4363
4364 $strSqlOrder = "";
4365 $arSqlOrder = array_unique($arSqlOrder);
4366 DelDuplicateSort($arSqlOrder);
4367
4368 for ($i = 0, $n = count($arSqlOrder); $i < $n; $i++)
4369 {
4370 if($i==0)
4371 $strSqlOrder = " ORDER BY ";
4372 else
4373 $strSqlOrder .= ",";
4374
4375 $strSqlOrder .= $arSqlOrder[$i];
4376 }
4377
4378 $strSql .= " WHERE 1=1 ".$strSqlSearch.$strSqlOrder;
4379
4380 $res = $DB->Query($strSql);
4381 $res = new _CMailLogDBRes($res);
4382 $res->is_filtered = $is_filtered;
4383 return $res;
4384 }
4385
4386 public static function ConvertRow($arr_log)
4387 {
4388 switch($arr_log["LOG_TYPE"])
4389 {
4390 case "FILTER_OK":
4391 $arr_log["MESSAGE_TEXT"] = GetMessage("MAIL_CL_RULE_RUN")." \"[".$arr_log["FILTER_ID"]."] ".mb_substr($arr_log["FILTER_NAME"], 0, 30).(mb_strlen($arr_log["FILTER_NAME"]) > 30?"...":"")."\" ";
4392 if($arr_log["MESSAGE"]=="R")
4393 $arr_log["MESSAGE_TEXT"] .= GetMessage("MAIL_CL_WHEN_CONNECT");
4394 else
4395 $arr_log["MESSAGE_TEXT"] .= GetMessage("MAIL_CL_WHEN_MANUAL");
4396 break;
4397 case "NEW_MESSAGE":
4398 $arr_log["MESSAGE_TEXT"] = GetMessage("MAIL_CL_NEW_MESSAGE")." ".$arr_log["MESSAGE"];
4399 break;
4400 case "SPAM":
4401 if($arr_log["FILTER_ID"]>0)
4402 $arr_log["MESSAGE_TEXT"] = "&nbsp;&nbsp;".GetMessage("MAIL_CL_RULE_ACT_SPAM");
4403 else
4404 $arr_log["MESSAGE_TEXT"] = GetMessage("MAIL_CL_ACT_SPAM");
4405 break;
4406 case "NOTSPAM":
4407 if($arr_log["FILTER_ID"]>0)
4408 $arr_log["MESSAGE_TEXT"] = "&nbsp;&nbsp;".GetMessage("MAIL_CL_RULE_ACT_NOTSPAM");
4409 else
4410 $arr_log["MESSAGE_TEXT"] = GetMessage("MAIL_CL_ACT_NOTSPAM");
4411 break;
4412 case "DO_PHP":
4413 $arr_log["MESSAGE_TEXT"] = "&nbsp;&nbsp;".GetMessage("MAIL_CL_RULE_ACT_PHP");
4414 break;
4415 case "MESSAGE_DELETED":
4416 $arr_log["MESSAGE_TEXT"] = "&nbsp;&nbsp;".GetMessage("MAIL_CL_RULE_ACT_DEL");
4417 break;
4418 case "FILTER_STOP":
4419 $arr_log["MESSAGE_TEXT"] = "&nbsp;&nbsp;".GetMessage("MAIL_CL_RULE_ACT_CANC");
4420 break;
4421 default:
4422 $arr_log["MESSAGE_TEXT"] = $arr_log["MESSAGE"];
4423 }
4424 return $arr_log;
4425 }
4426}
4427
4429{
4431 {
4432 parent::__construct($res);
4433 }
4434
4435 function Fetch()
4436 {
4437 if($arr_log = parent::Fetch())
4438 return CMailLog::ConvertRow($arr_log);
4439
4440 return false;
4441 }
4442}
$sum
Определения checkout.php:6
$type
Определения options.php:106
$db_res
Определения options_user_settings.php:8
$ar_res
Определения options_user_settings_set.php:16
if(! $messageFields||!isset($messageFields['message_id'])||!isset($messageFields['status'])||!CModule::IncludeModule("messageservice")) $messageId
Определения callback_ismscenter.php:26
global $APPLICATION
Определения include.php:80
$arResult
Определения generate_coupon.php:16
$login
Определения change_password.php:8
Определения mail.php:2622
fetch()
Определения mail.php:2628
__construct($res)
Определения mail.php:2623
Определения mail.php:190
Fetch()
Определения mail.php:196
__construct($res)
Определения mail.php:191
Определения mail.php:4429
Fetch()
Определения mail.php:4435
__construct($res)
Определения mail.php:4430
static registerAttachment(array $attachment)
Определения storage.php:145
static unregisterAttachment($fileId)
Определения storage.php:190
static addContactsBatch($contactsData)
Определения mailcontact.php:82
static parseMessage(array &$message)
Определения message.php:340
static getConnection($name="")
Определения application.php:638
Определения ini.php:6
static get($moduleId, $name, $default="", $siteId=false)
Определения option.php:30
static getInstance()
Определения eventmanager.php:31
static getFileContents($path)
Определения file.php:255
static includeModule($moduleName)
Определения loader.php:67
Определения emoji.php:10
Определения mail.php:227
GetMessage($mailbox_id, $msgnum, $msguid, $session_id)
Определения mail.php:1081
GetResponse($bMultiline=false, $bSkipFirst=true)
Определения mail.php:784
static GetList($arOrder=[], $arFilter=[])
Определения mail.php:239
Check($server, $port, $use_tls, $login, $passw)
Определения mail.php:842
static CheckFields($arFields, $ID=false)
Определения mail.php:443
GetResponseBody()
Определения mail.php:828
DeleteMessage($msgnum)
Определения mail.php:1105
static Delete($ID)
Определения mail.php:692
GetResponseString()
Определения mail.php:833
Connect($mailbox_id)
Определения mail.php:880
static Add($arFields)
Определения mail.php:545
static GetByID($ID)
Определения mail.php:371
$response_body
Определения mail.php:234
$mailbox_id
Определения mail.php:235
$last_result
Определения mail.php:232
$resp
Определения mail.php:231
static SMTPReload()
Определения mail.php:757
$pop3_conn
Определения mail.php:228
GetPassword($p)
Определения mail.php:838
$deleted_mess_count
Определения mail.php:237
$new_mess_count
Определения mail.php:236
SendCommand($command)
Определения mail.php:764
$mess_count
Определения mail.php:229
static Update($ID, $arFields)
Определения mail.php:590
static CheckMailAgent($ID)
Определения mail.php:410
CheckMail($mailbox_id=false)
Определения mail.php:376
$response
Определения mail.php:233
$mess_size
Определения mail.php:230
Определения mail.php:1320
static decodeMessageBody($header, $body, $charset)
Определения mail.php:1531
static parseHeader($header, $charset)
Определения mail.php:1524
static MarkAsSpam($ID, $bIsSPAM=true, $arRow=false)
Определения mail.php:2253
static isLongMessageBody(?string &$messageBody)
Определения mail.php:2400
static Delete($id)
Определения mail.php:2225
static GetSpamRating($msgid, $arRow=false)
Определения mail.php:1497
static addAttachment($arFields)
Определения mail.php:2310
static GetByID($ID)
Определения mail.php:1492
static getTextHtmlBlock(string &$body)
Определения mail.php:2480
static GetList($arOrder=Array(), $arFilter=Array(), $bCnt=false)
Определения mail.php:1323
static Add($arFields, $mailboxID=false)
Определения mail.php:2040
const MAX_LENGTH_MESSAGE_BODY
Определения mail.php:1321
static Update($ID, $arFields, $mailboxID=false)
Определения mail.php:2119
static prepareLongMessage(&$messageBody, &$messageBodyHtml)
Определения mail.php:2532
static addMessage($mailboxId, $message, $charset, $params=array())
Определения mail.php:1696
static parseMessage($message, $charset)
Определения mail.php:1560
static getBodyMaxLength()
Определения mail.php:2415
static getPartBody(&$part)
Определения mail.php:2573
static saveMessage(int $mailboxId, &$message, &$header, &$bodyHtml, &$bodyText, &$attachments, $params=array())
Определения mail.php:1703
Определения mail.php:2813
static extractAllMailAddresses($emails)
Определения mail.php:3106
static Decrypt($str, $key=false)
Определения mail.php:3072
static ByteXOR($a, $b, $l)
Определения mail.php:3055
static checkImapMailbox($server, $port, $use_tls, $login, $password, &$error)
Определения mail.php:3128
static ExtractMailAddress($email)
Определения mail.php:3118
static MkOperationFilter($key)
Определения mail.php:2914
static convertCharset($str, $from, $to)
Определения mail.php:2814
static FilterCreateEx($fname, $vals, $type, &$bFullJoin, $cOperationType=false, $bSkipEmpty=true)
Определения mail.php:2957
static FilterCreate($fname, $vals, $type, $cOperationType=false, $bSkipEmpty=true)
Определения mail.php:2952
static BinMD5($val)
Определения mail.php:3067
static uue_decode($str)
Определения mail.php:2861
static Crypt($str, $key=false)
Определения mail.php:3090
Определения dbresult.php:88
Определения mail.php:2650
static GetByID($ID)
Определения mail.php:2759
Delete($id)
Определения mail.php:2764
static getContents($attachment)
Определения mail.php:2783
static GetList($arOrder=Array(), $arFilter=Array())
Определения mail.php:2651
Определения mail.php:138
static ErrCount()
Определения mail.php:179
static GetErrors()
Определения mail.php:160
static ResetErrors()
Определения mail.php:139
static GetLastError($type=false)
Определения mail.php:152
static GetErrorsText($delim="<br>")
Определения mail.php:166
static SetError($ID, $TITLE="", $DESC="")
Определения mail.php:145
Определения mail.php:4068
static Delete($ID)
Определения mail.php:4155
GetByID($ID)
Определения mail.php:4149
static Add($arFields)
Определения mail.php:4203
static SetConditions($FILTER_ID, $CONDITIONS, $bClearOther=true)
Определения mail.php:4163
static GetList($arOrder=Array(), $arFilter=Array())
Определения mail.php:4069
static Update($ID, $arFields)
Определения mail.php:4220
Определения mail.php:3154
static Filter($arFields, $event, $FILTER_ID=false, $PARENT_FILTER_ID=false)
Определения mail.php:3477
static FilterMessage($message_id, $event, $FILTER_ID=false)
Определения mail.php:3822
static DoPHPAction($id, $action, &$arMessageFields)
Определения mail.php:3940
static CheckFields($arFields, $ID=false)
Определения mail.php:3324
static getWords($message, $max_words)
Определения mail.php:3914
static Delete($ID)
Определения mail.php:3461
static GetSpamRating($message)
Определения mail.php:3843
static Add($arFields)
Определения mail.php:3373
static GetByID($ID)
Определения mail.php:3271
static GetList($arOrder=Array(), $arFilter=Array(), $bCnt=false)
Определения mail.php:3155
static RecalcSpamRating()
Определения mail.php:3831
static GetFilterList($id="")
Определения mail.php:4030
static CheckPHP($code, $field_name)
Определения mail.php:3277
static DeleteFromSpamBase($message, $bIsSPAM=true)
Определения mail.php:3945
static CheckConditionTypes($fields)
Определения mail.php:3294
static Update($ID, $arFields)
Определения mail.php:3411
static MarkAsSpam($message, $bIsSPAM=true)
Определения mail.php:3950
static SpamAction($message, $bIsSPAM, $bDelete=false)
Определения mail.php:3955
Определения mail.php:1117
$arHeaderLines
Определения mail.php:1119
$filename
Определения mail.php:1122
MultipartType()
Определения mail.php:1283
static ConvertHeader($encoding, $type, $str, $charset)
Определения mail.php:1125
$MultipartType
Определения mail.php:1122
$strHeader
Определения mail.php:1120
$boundary
Определения mail.php:1122
$arHeader
Определения mail.php:1118
GetHeader($type)
Определения mail.php:1293
$content_id
Определения mail.php:1123
$bMultipart
Определения mail.php:1121
IsMultipart()
Определения mail.php:1278
DecodeHeader($str, $charset_to, $charset_document)
Определения mail.php:1138
GetBoundary()
Определения mail.php:1288
Parse($message_header, $charset)
Определения mail.php:1163
$content_type
Определения mail.php:1122
$charset
Определения mail.php:1122
Определения mail.php:18
const ERR_API_DENIED
Определения mail.php:23
const ERR_API_BAD_NAME
Определения mail.php:31
const ERR_API_OLD_TOKEN
Определения mail.php:37
const F_DOMAIN_REG
Определения mail.php:46
const ERR_API_OP_DENIED
Определения mail.php:36
static getErrorMessage($code)
Определения mail.php:48
const ERR_API_BAD_PASSWORD
Определения mail.php:32
const ERR_API_LONG_PASSWORD
Определения mail.php:35
const ERR_DEFAULT
Определения mail.php:19
const ERR_API_LONG_NAME
Определения mail.php:34
const ERR_API_PROHIBITED_DOMAIN
Определения mail.php:41
const ERR_API_EMPTY_PASSWORD
Определения mail.php:29
static onUserDelete($id)
Определения mail.php:107
const ERR_ENTRY_NOT_FOUND
Определения mail.php:43
const ERR_API_EMPTY_DOMAIN
Определения mail.php:27
static onUserUpdate($arFields)
Определения mail.php:97
const ERR_API_BAD_DOMAIN
Определения mail.php:40
static option($name, $value=null)
Определения mail.php:114
const ERR_API_DOMAINLIST_EMPTY
Определения mail.php:24
const ERR_API_EMPTY_NAME
Определения mail.php:28
const F_DOMAIN_LOGO
Определения mail.php:45
const ERR_API_DOMAIN_OCCUPIED
Определения mail.php:39
const ERR_API_USER_NOTFOUND
Определения mail.php:26
const ERR_API_PASSWORD_LIKELOGIN
Определения mail.php:33
const ERR_DB
Определения mail.php:20
const ERR_API_DEFAULT
Определения mail.php:22
const ERR_API_NAME_OCCUPIED
Определения mail.php:25
const ERR_API_SHORT_PASSWORD
Определения mail.php:30
Определения mail.php:4249
static AddMessage($arFields)
Определения mail.php:4250
static ConvertRow($arr_log)
Определения mail.php:4386
static Delete($ID)
Определения mail.php:4266
static GetList($arOrder=Array(), $arFilter=Array())
Определения mail.php:4274
Определения mail.php:1301
fetch()
Определения mail.php:1303
static IsSizeAllowed($size)
Определения mail.php:50
Определения mail.php:15
Определения user.php:6037
global $CACHE_MANAGER
Определения clear_component_cache.php:7
$options
Определения commerceml2.php:49
$str
Определения commerceml2.php:63
$arFields
Определения dblapprove.php:5
$data['IS_AVAILABLE']
Определения .description.php:13
$filename
Определения file_edit.php:47
$arr
Определения file_new.php:624
</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
GetFilterQuery($field, $val, $procent="Y", $ex_sep=array(), $clob="N", $div_fields="Y", $clob_upper="N")
Определения filter_tools.php:383
$result
Определения get_property_values.php:14
if($ajaxMode) $ID
Определения get_user.php:27
$entity
$p
Определения group_list_element_edit.php:23
$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
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
global $BX_MAIL_FILTER_CACHE
Определения mail.php:3149
global $B_MAIL_MAX_ALLOWED
Определения mail.php:13
global $BX_MAIL_ERRORs
Определения mail.php:13
global $BX_MAIL_SPAM_CNT
Определения mail.php:3149
const B_MAIL_SAVE_SRC
Определения constants.php:5
const B_MAIL_CHECK_SPAM
Определения constants.php:3
const B_MAIL_TIMEOUT
Определения constants.php:2
const B_MAIL_MIN_CNT
Определения constants.php:11
const B_MAIL_WORD_CNT
Определения constants.php:10
const B_MAIL_SAVE_ATTACHMENTS
Определения constants.php:7
$emails
Определения mail_entry.php:72
$l
Определения options.php:783
CheckDirPath($path)
Определения tools.php:2707
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
AddMessage2Log($text, $module='', $traceDepth=6, $showArgs=false)
Определения tools.php:3941
htmlspecialcharsback($str)
Определения tools.php:2693
DelDuplicateSort(&$arSort)
Определения tools.php:2055
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
is_set($a, $k=false)
Определения tools.php:2133
GetMessage($name, $aReplace=null)
Определения tools.php:3397
$name
Определения menu_edit.php:35
$TITLE
Определения menu_edit.php:223
$order
Определения payment.php:8
$email
Определения payment.php:49
$message
Определения payment.php:8
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$ar
Определения options.php:199
if(empty($signedUserToken)) $key
Определения quickway.php:257
$contentType
Определения quickway.php:301
$text
Определения template_pdf.php:79
$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
else $a
Определения template.php:137
$val
Определения options.php:1793
$password
Определения result.php:7
$matches
Определения index.php:22
$error
Определения subscription_card_product.php:20
$k
Определения template_pdf.php:567
$action
Определения file_dialog.php:21
$GLOBALS['_____370096793']
Определения update_client.php:1
$n
Определения update_log.php:107
$arFilter
Определения user_search.php:106
$fields
Определения yandex_run.php:501