Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
controller.php
1<?php
2
4
15use CIMNotify;
16use CModule;
17
24{
25 public const STATUS_DEFERED = 'defered';
26 public const STATUS_BOUNCED = 'bounced';
27 public const STATUS_DELIVERED = 'delivered';
28
29 public const DESC_AUTH = 'AUTH_ERROR';
30 public const DESC_UNKNOWN_USER = 'UNKNOWN_USER';
31 public const DESC_UNROUTEABLE = 'UNROUTEABLE';
32 private const DESC_SMTP_LIMITED = 'SMTP_LIMITED';
33
35 protected $id;
36
38 protected $result;
39
41 protected $config;
42
44 protected $address;
45
47 protected $blacklist = [];
48
50 protected $smtpLimited = [];
51
53 protected static $answerExceptions = true;
54
56 protected $enableItemErrors = false;
57
59 protected $countItems = 0;
61 protected $countItemsProcessed = 0;
63 protected $countItemsError = 0;
64
72 public static function run($data = null, array $parameters = [])
73 {
74 $request = Context::getCurrent()->getRequest();
75 if ($data === null)
76 {
77 $data = $request->getPostList()->getRaw('data');
78 }
79 if (!isset($parameters['IGNORE_ITEM_ERRORS']))
80 {
81 $parameters['ENABLE_ITEM_ERRORS'] = mb_strtoupper($request->get('enableItemErrors')) === 'Y';
82 }
83
84 $instance = new self();
85 if ($parameters['ENABLE_ITEM_ERRORS'])
86 {
87 $instance->enableItemErrors();
88 }
89
90 try
91 {
92 if (empty($data))
93 {
94 self::giveAnswer(true, 'No input data.');
95 }
96
97 try
98 {
99 $data = Json::decode($data);
100 }
101 catch (\Exception $exception)
102 {
103 }
104
105 if (!is_array($data))
106 {
107 self::giveAnswer(true, 'Wrong data.');
108 }
109
110 if (!isset($data['list']) || !is_array($data['list']))
111 {
112 self::giveAnswer(true, 'Parameter `list` required.');
113 }
114
115 $instance->processList($data['list']);
116
117 self::giveAnswer(false, ['list' => $instance->getCounters()]);
118 }
119 catch (SystemException $exception)
120 {
121 self::giveAnswer(
122 true,
123 [
124 'text' => self::$answerExceptions ? $exception->getMessage() : null,
125 'list' => $instance->getCounters()
126 ]
127 );
128 }
129 }
130
138 public static function giveAnswer($isError = false, $answer = null)
139 {
140 $response = Context::getCurrent()->getResponse();
141 $response->addHeader('Status', $isError ? '422' : '200');
142 $response->addHeader('Content-Type', 'application/json');
143
144 if (!is_array($answer))
145 {
146 $answer = [
147 'text' => $answer ?: null
148 ];
149 }
150 $answer['error'] = $isError;
151 if (empty($answer['text']))
152 {
153 $answer['text'] = $isError ? 'Unknown error' : 'Success';
154 }
155 $answer = Json::encode($answer);
156
157 \CMain::FinalActions($answer);
158 exit;
159 }
160
164 public function __construct()
165 {
166 $this->config = new Config();
167 $this->result = new Result();
168 $this->address = new Address();
169 }
170
176 public function enableItemErrors()
177 {
178 $this->enableItemErrors = true;
179 return $this;
180 }
181
182 protected function validateItem($item)
183 {
184 if (empty($item['id']))
185 {
186 throw new ArgumentException('Field `id` is required for item.');
187 }
188 if(!preg_match("/[a-zA-Z0-1=]{3,}/", $item['id']))
189 {
190 throw new ArgumentException('Field `id` has disallowed chars.');
191 }
192
193 if (empty($item['sign']))
194 {
195 throw new ArgumentException("Field `sign` is required for item with id `{$item['id']}`.");
196 }
197
198 if (empty($item['status']))
199 {
200 throw new ArgumentException("Field `status` is required for item with id `{$item['id']}`.");
201 }
202
203 if (empty($item['email']))
204 {
205 throw new ArgumentException("Field `email` is required for item with id `{$item['id']}`.");
206 }
207 }
208
216 public function processList($list)
217 {
218 $this->countItems = count($list);
219
220 $this->blacklist = [];
221 $this->smtpLimited = [];
222 foreach ($list as $index => $item)
223 {
224 $this->countItemsProcessed++;
225 try
226 {
227 $result = $this->processItem($item);
228 if (!$result)
229 {
230 $this->countItemsError++;
231 }
232 }
233 catch (SystemException $exception)
234 {
235 $this->countItemsError++;
236
237 if ($this->enableItemErrors)
238 {
239 throw $exception;
240 }
241 }
242 }
243
244 Internal\BlacklistTable::insertBatch($this->blacklist);
245 $this->decreaseLimit();
246 }
247
253 public function decreaseLimit()
254 {
255 if (!$this->smtpLimited)
256 {
257 return;
258 }
259
260 foreach ($this->smtpLimited as $email)
261 {
262 Sender::setEmailLimit(
263 $email,
264 SenderSendCounter::DEFAULT_LIMIT,
265 false,
266 );
267 }
268 }
269
277 public function processItem($item)
278 {
279 $this->validateItem($item);
280
281 $this->config->unpackId($item['id']);
282 if (!$this->config->verifySignature($item['sign']))
283 {
284 throw new SystemException('Item parameter `sign` is invalid.');
285 }
286
287 if (!$this->config->getEntityId())
288 {
289 return false;
290 }
291
292 $email = $this->address->set($item['email'])->getEmail();
293 if (!$email)
294 {
295 return false;
296 }
297
298 if (!empty($item['sender']) && self::isSmtpLimited($item['statusDescription']) )
299 {
300 $this->smtpLimited[] = $this->address->set($item['sender'])->getEmail();
301 }
302
303 $this->result
304 ->setModuleId($this->config->getModuleId())
305 ->setEntityType($this->config->getEntityType())
306 ->setEntityId($this->config->getEntityId())
307 ->setEmail($email)
308 ->setDateSent((int) $item['completedAt'])
309 ->setError(self::isStatusError($item['status']))
310 ->setPermanentError(self::isStatusPermanentError($item['status']))
311 ->setBlacklistable(self::isBlacklistable($item['statusDescription']))
312 ->setDescription($item['statusDescription'])
313 ->setMessage($item['message']);
314
315 if ($this->result->isPermanentError() && $this->result->isBlacklistable())
316 {
317 $this->blacklist[] = $this->result->getEmail();
318 }
319
320 $this->result->sendEvent();
321
322 return true;
323 }
324
330 public function getCounters()
331 {
332 return [
333 'all' => $this->countItems,
334 'processed' => $this->countItemsProcessed,
335 'errors' => $this->countItemsError,
336 ];
337 }
338
345 public static function isStatusError($status)
346 {
347 return in_array($status, [self::STATUS_DEFERED, self::STATUS_BOUNCED]);
348 }
349
356 public static function isStatusPermanentError($status)
357 {
358 return $status === self::STATUS_BOUNCED;
359 }
360
367 public static function isBlacklistable($description)
368 {
369 return $description && in_array($description, [self::DESC_UNKNOWN_USER, self::DESC_UNROUTEABLE]);
370 }
371
378 private static function isSmtpLimited(string $description)
379 {
380 return $description && in_array($description, [self::DESC_SMTP_LIMITED], true);
381 }
382
383}
static getCurrent()
Definition context.php:241
static run($data=null, array $parameters=[])
static isBlacklistable($description)
static giveAnswer($isError=false, $answer=null)