1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
controller.php
См. документацию.
1<?php
2
3namespace Bitrix\Main\Mail\Callback;
4
5use Bitrix\Mail\Helper\OAuth;
6use Bitrix\Main\ArgumentException;
7use Bitrix\Main\Context;
8use Bitrix\Main\Loader;
9use Bitrix\Main\LoaderException;
10use Bitrix\Main\Mail\Address;
11use Bitrix\Main\Mail\Smtp\CloudOAuthRefreshData;
12use Bitrix\Main\Mail\Smtp\OAuthConfigPreparer;
13use Bitrix\Main\SystemException;
14use Bitrix\Main\Web\Json;
15use Bitrix\Main\Mail\Internal;
16use Bitrix\Main\Mail\SenderSendCounter;
17use Bitrix\Main\Mail\Sender;
18
25{
26 public const STATUS_DEFERED = 'defered';
27 public const STATUS_BOUNCED = 'bounced';
28 public const STATUS_DELIVERED = 'delivered';
29
30 public const DESC_AUTH = 'AUTH_ERROR';
31 public const DESC_UNKNOWN_USER = 'UNKNOWN_USER';
32 public const DESC_UNROUTEABLE = 'UNROUTEABLE';
33 private const DESC_SMTP_LIMITED = 'SMTP_LIMITED';
34
36 protected $id;
37
39 protected $result;
40
42 protected $config;
43
45 protected $address;
46
48 protected $blacklist = [];
49
51 protected $smtpLimited = [];
52
54 protected static $answerExceptions = true;
55
57 protected $enableItemErrors = false;
58
60 protected $countItems = 0;
62 protected $countItemsProcessed = 0;
64 protected $countItemsError = 0;
65
66 protected array $refreshedTokens = [];
67
75 public static function run($data = null, array $parameters = [])
76 {
77 $request = Context::getCurrent()->getRequest();
78 if ($data === null)
79 {
80 $data = $request->getPostList()->getRaw('data');
81 }
82 if (!isset($parameters['IGNORE_ITEM_ERRORS']))
83 {
84 $parameters['ENABLE_ITEM_ERRORS'] = mb_strtoupper($request->get('enableItemErrors')) === 'Y';
85 }
86
87 $instance = new self();
88 if ($parameters['ENABLE_ITEM_ERRORS'])
89 {
90 $instance->enableItemErrors();
91 }
92
93 try
94 {
95 if (empty($data))
96 {
97 self::giveAnswer(true, 'No input data.');
98 }
99
100 try
101 {
102 $data = Json::decode($data);
103 }
104 catch (\Exception $exception)
105 {
106 }
107
108 if (!is_array($data))
109 {
110 self::giveAnswer(true, 'Wrong data.');
111 }
112
113 if (!isset($data['list']) || !is_array($data['list']))
114 {
115 self::giveAnswer(true, 'Parameter `list` required.');
116 }
117
118 $instance->processList($data['list']);
119
120 self::giveAnswer(false, ['list' => $instance->getCounters()]);
121 }
122 catch (SystemException $exception)
123 {
124 self::giveAnswer(
125 true,
126 [
127 'text' => self::$answerExceptions ? $exception->getMessage() : null,
128 'list' => $instance->getCounters()
129 ]
130 );
131 }
132 }
133
141 public static function giveAnswer($isError = false, $answer = null)
142 {
143 $response = Context::getCurrent()->getResponse();
144 $response->addHeader('Status', $isError ? '422' : '200');
145 $response->addHeader('Content-Type', 'application/json');
146
147 if (!is_array($answer))
148 {
149 $answer = [
150 'text' => $answer ?: null
151 ];
152 }
153 $answer['error'] = $isError;
154 if (empty($answer['text']))
155 {
156 $answer['text'] = $isError ? 'Unknown error' : 'Success';
157 }
158 $answer = Json::encode($answer);
159
160 \CMain::FinalActions($answer);
161 exit;
162 }
163
167 public function __construct()
168 {
169 $this->config = new Config();
170 $this->result = new Result();
171 $this->address = new Address();
172 }
173
179 public function enableItemErrors()
180 {
181 $this->enableItemErrors = true;
182 return $this;
183 }
184
185 protected function validateItem($item)
186 {
187 if (empty($item['id']))
188 {
189 throw new ArgumentException('Field `id` is required for item.');
190 }
191 if(!preg_match("/[a-zA-Z0-1=]{3,}/", $item['id']))
192 {
193 throw new ArgumentException('Field `id` has disallowed chars.');
194 }
195
196 if (empty($item['sign']))
197 {
198 throw new ArgumentException("Field `sign` is required for item with id `{$item['id']}`.");
199 }
200
201 if (empty($item['status']))
202 {
203 throw new ArgumentException("Field `status` is required for item with id `{$item['id']}`.");
204 }
205
206 if (empty($item['email']))
207 {
208 throw new ArgumentException("Field `email` is required for item with id `{$item['id']}`.");
209 }
210 }
211
219 public function processList($list)
220 {
221 $this->countItems = count($list);
222
223 $this->blacklist = [];
224 $this->smtpLimited = [];
225 foreach ($list as $index => $item)
226 {
227 $this->countItemsProcessed++;
228 try
229 {
230 $result = $this->processItem($item);
231 if (!$result)
232 {
233 $this->countItemsError++;
234 }
235 }
236 catch (SystemException $exception)
237 {
238 $this->countItemsError++;
239
240 if ($this->enableItemErrors)
241 {
242 throw $exception;
243 }
244 }
245 }
246
247 Internal\BlacklistTable::insertBatch($this->blacklist);
248 $this->decreaseLimit();
249 }
250
256 public function decreaseLimit()
257 {
258 if (!$this->smtpLimited)
259 {
260 return;
261 }
262
263 foreach ($this->smtpLimited as $email)
264 {
265 Sender::setEmailLimit(
266 $email,
268 false,
269 );
270 }
271 }
272
280 public function processItem($item)
281 {
282 $this->validateItem($item);
283
284 $this->config->unpackId($item['id']);
285 if (!$this->config->verifySignature($item['sign']))
286 {
287 throw new SystemException('Item parameter `sign` is invalid.');
288 }
289
290 if (!$this->config->getEntityId())
291 {
292 return false;
293 }
294
295 $email = $this->address->set($item['email'])->getEmail();
296 if (!$email)
297 {
298 return false;
299 }
300
301 $this->processAsRefreshRequest($item);
302
303 if (!empty($item['sender']) && self::isSmtpLimited($item['statusDescription']) )
304 {
305 $this->smtpLimited[] = $this->address->set($item['sender'])->getEmail();
306 }
307
308 $this->result
309 ->setModuleId($this->config->getModuleId())
310 ->setEntityType($this->config->getEntityType())
311 ->setEntityId($this->config->getEntityId())
312 ->setEmail($email)
313 ->setDateSent((int) $item['completedAt'])
314 ->setError(self::isStatusError($item['status']))
315 ->setPermanentError(self::isStatusPermanentError($item['status']))
316 ->setBlacklistable(self::isBlacklistable($item['statusDescription']))
317 ->setDescription($item['statusDescription'])
318 ->setMessage($item['message']);
319
320 if ($this->result->isPermanentError() && $this->result->isBlacklistable())
321 {
322 $this->blacklist[] = $this->result->getEmail();
323 }
324
325 $this->result->sendEvent();
326
327 return true;
328 }
329
335 public function getCounters(): array
336 {
337 $result = [
338 'all' => $this->countItems,
339 'processed' => $this->countItemsProcessed,
340 'errors' => $this->countItemsError,
341 ];
342
343 if ($this->refreshedTokens)
344 {
345 $result['refreshedTokens'] = $this->refreshedTokens;
346 }
347
348 return $result;
349 }
350
357 public static function isStatusError($status)
358 {
359 return in_array($status, [self::STATUS_DEFERED, self::STATUS_BOUNCED]);
360 }
361
368 public static function isStatusPermanentError($status)
369 {
370 return $status === self::STATUS_BOUNCED;
371 }
372
379 public static function isBlacklistable($description)
380 {
381 return $description && in_array($description, [self::DESC_UNKNOWN_USER, self::DESC_UNROUTEABLE]);
382 }
383
390 private static function isSmtpLimited(string $description)
391 {
392 return $description && in_array($description, [self::DESC_SMTP_LIMITED], true);
393 }
394
399 private function processAsRefreshRequest(array $item): void
400 {
401 if (empty($item['refreshUid']) || !isset($item['refreshExpires']) || empty($item['refreshSign']))
402 {
403 return;
404 }
405
406 $uid = (string)base64_decode($item['refreshUid']);
407 $data = new CloudOAuthRefreshData($uid, (int)$item['refreshExpires']);
408 if (!$data->isSignValid((string)$item['refreshSign']))
409 {
410 throw new SystemException('Invalid refresh oauth signature');
411 }
412
413 if (!Loader::includeModule('mail'))
414 {
415 throw new SystemException('Module mail not installed');
416 }
417
418 $mailOAuth = OAuth::getInstanceByMeta($uid);
419 if (!$mailOAuth || !$mailOAuth->getStoredUid())
420 {
421 throw new SystemException('Incorrect refresh meta');
422 }
423
424 $expireGapSeconds = (new OAuthConfigPreparer())->getOAuthTokenExpireGapSeconds();
425 $token = $mailOAuth->getStoredToken(null, $expireGapSeconds);
426 if (empty($token))
427 {
428 throw new SystemException('Cannot refresh token');
429 }
430
431 $expires = $defaultExpires = time() + $expireGapSeconds;
432 $oauthEntity = $mailOAuth->getOAuthEntity();
433 if (is_object($oauthEntity) && method_exists($oauthEntity, 'getTokenData'))
434 {
435 $expires = $oauthEntity->getTokenData()['expires_in'] ?? $defaultExpires;
436 }
437
438 $this->refreshedTokens[] = [
439 'uid' => $item['refreshUid'],
440 'accessToken' => $token,
441 'expires' => (int)$expires,
442 ];
443 }
444
445}
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
Определения result.php:20
static includeModule($moduleName)
Определения loader.php:67
static isStatusPermanentError($status)
Определения controller.php:368
static run($data=null, array $parameters=[])
Определения controller.php:75
static isBlacklistable($description)
Определения controller.php:379
static isStatusError($status)
Определения controller.php:357
static giveAnswer($isError=false, $answer=null)
Определения controller.php:141
static insertBatch(array $list)
Определения blacklist.php:100
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
if(Loader::includeModule( 'bitrix24')) elseif(Loader::includeModule('intranet') &&CIntranetUtils::getPortalZone() !=='ru') $description
Определения .description.php:24
$uid
Определения hot_keys_act.php:8
$status
Определения session.php:10
$email
Определения payment.php:49
$instance
Определения ps_b24_final.php:14
</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
$response
Определения result.php:21