PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.1
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
← All changes | back/Helpers/BounceHelper.php +0 -1480 10.11.1 → 11.1.1 View file →
@@ -1,1480 +1,0 @@
1 -<?php
2 -
3 -namespace AcyMailing\Helpers;
4 -
5 -use AcyMailerPhp\OAuth;
6 -use AcyMailing\Classes\HistoryClass;
7 -use AcyMailing\Classes\ListClass;
8 -use AcyMailing\Classes\MailClass;
9 -use AcyMailing\Classes\RuleClass;
10 -use AcyMailing\Classes\UserClass;
11 -use AcyMailing\Core\AcymObject;
12 -use AcyMailing\Libraries\Pear\Pop3;
13 -use AcyMailing\Types\CharsetType;
14 -
15 -class BounceHelper extends AcymObject
16 -{
17 - const HOSTS_NEEDING_OAUTH = [
18 - 'imap.gmail.com',
19 - 'outlook.office365.com',
20 - ];
21 -
22 - const MESSAGE_TYPE_SUCCESS = 0;
23 - const MESSAGE_TYPE_INFO = 1;
24 - const MESSAGE_TYPE_ERROR = 2;
25 -
26 - private string $server;
27 - private string $username;
28 - private string $password;
29 - private string $port;
30 - private string $connectMethod;
31 - private string $secureMethod;
32 - private bool $selfSigned;
33 - private int $timeout;
34 - private string $oAuthToken;
35 -
36 - private array $allowed_extensions = [];
37 - protected int $nbMessages = 0;
38 - public bool $report = false;
39 - private MailerHelper $mailer;
40 - private $mailbox;
41 - public $_message;
42 - private UserClass $userClass;
43 - private array $blockedUsers = [];
44 - private array $deletedUsers = [];
45 - private array $bounceMessages = [];
46 - private bool $usePear = false;
47 - private string $detectEmail;
48 - private string $detectEmail2 = '/(([a-z0-9\-]+\.)+[a-z0-9]{2,8})\/([a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*)/i';
49 - public array $messages = [];
50 - public int $stoptime = 0;
51 - protected bool $mod_security2 = false;
52 - public int $obend = 0;
53 - public object $action;
54 - public array $attachments = [];
55 -
56 - private array $inlineImages = [];
57 - private array $allCharsets;
58 - private bool $errorDuringActions = false;
59 -
60 - private RuleClass $ruleClass;
61 - private MailClass $mailClass;
62 - private HistoryClass $historyClass;
63 - private EncodingHelper $encodingHelper;
64 -
65 - private array $allLists;
66 -
67 - public function __construct()
68 - {
69 - parent::__construct();
70 -
71 - $this->mailer = new MailerHelper();
72 - $this->ruleClass = new RuleClass();
73 - $this->userClass = new UserClass();
74 - $this->mailClass = new MailClass();
75 - $this->historyClass = new HistoryClass();
76 - $this->encodingHelper = new EncodingHelper();
77 -
78 - $this->mailer->report = false;
79 - $this->mailer->alreadyCheckedAddresses = true;
80 -
81 - $charsetType = new CharsetType();
82 - $this->allCharsets = $charsetType->charsets;
83 -
84 - $this->allowed_extensions = explode(',', $this->config->get('allowed_files', ''));
85 - $this->detectEmail = '/'.acym_getEmailRegex(false, true).'/i';
86 - }
87 -
88 - public function init(array $config = []): bool
89 - {
90 - if (empty($config)) {
91 - $config = [
92 - 'server' => $this->config->get('bounce_server'),
93 - 'username' => $this->config->get('bounce_username'),
94 - 'password' => $this->config->get('bounce_password'),
95 - 'port' => $this->config->get('bounce_port'),
96 - 'connect_method' => $this->config->get('bounce_connection'),
97 - 'secure_method' => $this->config->get('bounce_secured'),
98 - 'self_signed' => $this->config->get('bounce_certif', false),
99 - 'timeout' => $this->config->get('bounce_timeout', 10),
100 - 'bounce_access_token' => $this->config->get('bounce_access_token'),
101 - ];
102 - }
103 -
104 - $this->server = $config['server'];
105 - $this->username = $config['username'];
106 - $this->password = $config['password'];
107 - $this->port = $config['port'];
108 - $this->connectMethod = $config['connect_method'];
109 - $this->secureMethod = $config['secure_method'];
110 - $this->selfSigned = $config['self_signed'];
111 - $this->timeout = intval($config['timeout']);
112 - $this->oAuthToken = $config['bounce_access_token'];
113 -
114 - if ($this->connectMethod === 'pear') {
115 - $this->usePear = true;
116 - include_once ACYM_LIBRARIES.'Pear'.DS.'Pop3.php';
117 -
118 - return true;
119 - }
120 -
121 - require_once ACYM_LIBRARIES.'Imap2'.DS.'bootstrap.php';
122 -
123 - if (extension_loaded('imap') || function_exists('imap_open')) {
124 - return true;
125 - }
126 -
127 - if ($this->report) {
128 - $prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : '';
129 - $extension = $prefix.'imap.'.PHP_SHLIB_SUFFIX;
130 -
131 - acym_display(
132 - 'The extension "'.$extension.'" could not be loaded, please change your PHP configuration to enable it or use the pop3 method without imap extension',
133 - 'error'
134 - );
135 - }
136 -
137 - return false;
138 - }
139 -
140 - public function connect(): bool
141 - {
142 - if ($this->usePear) {
143 - return $this->connectPear();
144 - }
145 -
146 - return $this->connectImap();
147 - }
148 -
149 - private function connectPear(): bool
150 - {
151 - ob_start();
152 -
153 - $this->mailbox = new Pop3();
154 -
155 - $timeout = $this->timeout;
156 - if (!empty($timeout)) {
157 - $this->mailbox->_timeout = $timeout;
158 - }
159 -
160 - $port = intval($this->port);
161 - $secure = $this->secureMethod;
162 - if (empty($port)) {
163 - if ($secure === 'ssl') {
164 - $port = '995';
165 - } else {
166 - $port = '110/pop3/notls';
167 - }
168 - }
169 -
170 - $serverName = trim($this->server);
171 -
172 - if (!empty($secure) && !strpos($serverName, '://')) {
173 - $serverName = $secure.'://'.$serverName;
174 - }
175 -
176 - if (!$this->mailbox->connect($serverName, $port)) {
177 - $warnings = ob_get_clean();
178 - if ($this->report) {
179 - $notification = [
180 - 'name' => 'error_connecting',
181 - 'removable' => 1,
182 - ];
183 - acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_CONNECTING', $this->server.' : '.$port), 'error', true, [$notification]);
184 - }
185 - if (!empty($warnings) && $this->report) {
186 - acym_display($warnings, 'warning');
187 - }
188 -
189 - return false;
190 - }
191 -
192 - $login = $this->mailbox->login(trim($this->username), trim($this->password), 'USER');
193 - if (empty($login) || isset($login->code)) {
194 - $warnings = ob_get_clean();
195 - if ($this->report) {
196 - acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_LOGIN', $this->username.':'.$this->password), 'error');
197 - }
198 - if (!empty($warnings) && $this->report) {
199 - acym_display($warnings, 'warning');
200 - }
201 -
202 - return false;
203 - }
204 -
205 - ob_end_clean();
206 -
207 - return true;
208 - }
209 -
210 - private function connectImap(): bool
211 - {
212 - if (empty($this->server)) {
213 - acym_enqueueMessage(acym_translation('ACYM_CONFIGURE_BOUNCE'), 'warning');
214 -
215 - return false;
216 - }
217 -
218 - ob_start();
219 - $buff = $this->callImapFunction('imap_alerts', []);
220 - $buff = $this->callImapFunction('imap_errors', []);
221 -
222 - $timeout = intval($this->timeout);
223 - if (!empty($timeout)) {
224 - $this->callImapFunction('imap_timeout', [IMAP_OPENTIMEOUT, $timeout]);
225 - }
226 -
227 - $port = intval($this->port);
228 - $secure = $this->secureMethod;
229 - $protocol = $this->connectMethod;
230 - $serverName = '{'.trim($this->server);
231 - if (empty($port)) {
232 - if ($protocol === 'imap') {
233 - $port = $secure === 'ssl' ? '993' : '143';
234 - } elseif ($protocol === 'pop3') {
235 - $port = $secure === 'ssl' ? '995' : '110';
236 - }
237 - }
238 -
239 - if (!empty($port)) {
240 - $serverName .= ':'.$port;
241 - }
242 -
243 - if ($this->server === 'outlook.office365.com') {
244 - $serverName .= '/imap';
245 - }
246 -
247 - if (!empty($secure)) {
248 - $serverName .= '/'.$secure;
249 - }
250 -
251 - $email = trim($this->username);
252 - if (strpos($email, '\\') !== false) {
253 - [$user, $authuser] = explode('\\', $email);
254 - [$x, $domain] = explode('@', $user);
255 - $serverName .= '/authuser='.$user.'/user='.$authuser.'@'.$domain;
256 - }
257 -
258 - if ($this->selfSigned) {
259 - $serverName .= '/novalidate-cert';
260 - }
261 -
262 - if (!empty($protocol) && $this->server !== 'outlook.office365.com') {
263 - $serverName .= '/service='.$protocol;
264 - }
265 - $serverName .= '}';
266 - if (in_array($this->server, self::HOSTS_NEEDING_OAUTH)) {
267 - $this->refreshToken();
268 - if (empty($this->oAuthToken)) {
269 - return false;
270 - }
271 - $this->mailbox = imap2_open($serverName, trim($this->username), $this->oAuthToken, OP_XOAUTH2);
272 - } else {
273 - $this->mailbox = imap_open($serverName, trim($this->username), trim($this->password), OP_SILENT);
274 - }
275 - $warnings = ob_get_clean();
276 -
277 - if ($this->report) {
278 - if (!$this->mailbox) {
279 - $notification = [
280 - 'name' => 'error_connecting',
281 - 'removable' => 1,
282 - ];
283 - acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_CONNECTING', $serverName), 'error', true, [$notification]);
284 - }
285 - if (!empty($warnings)) {
286 - acym_enqueueMessage($warnings, 'warning');
287 - }
288 - }
289 -
290 - return (bool)$this->mailbox;
291 - }
292 -
293 - public function getNBMessages(): int
294 - {
295 - if ($this->usePear) {
296 - $this->nbMessages = intval($this->mailbox->numMsg());
297 - } else {
298 - $this->nbMessages = intval($this->callImapFunction('imap_num_msg', [$this->mailbox]));
299 - }
300 -
301 - return $this->nbMessages;
302 - }
303 -
304 - protected function getMessage(int $msgNB)
305 - {
306 - if ($this->usePear) {
307 - $message = new \stdClass();
308 - $message->headerString = $this->mailbox->getRawHeaders($msgNB);
309 - if (empty($message->headerString)) {
310 - return false;
311 - }
312 - } else {
313 - $message = $this->callImapFunction('imap_headerinfo', [$this->mailbox, $msgNB]);
314 - }
315 -
316 - return $message;
317 - }
318 -
319 - protected function deleteMessage($msgNB): void
320 - {
321 - if ($this->usePear) {
322 - $this->mailbox->deleteMsg($msgNB);
323 - } else {
324 - $this->callImapFunction('imap_delete', [$this->mailbox, $msgNB]);
325 - $this->callImapFunction('imap_expunge', [$this->mailbox]);
326 - }
327 - }
328 -
329 - public function close(): void
330 - {
331 - if ($this->usePear) {
332 - $this->mailbox->disconnect();
333 - } else {
334 - $this->callImapFunction('imap_close', [$this->mailbox]);
335 - }
336 - }
337 -
338 - public function decodeMessage(bool $attachments = false): bool
339 - {
340 - if ($this->usePear) {
341 - return $this->decodeMessagePear($attachments);
342 - } else {
343 - return $this->decodeMessageImap($attachments);
344 - }
345 - }
346 -
347 - private function decodeMessagePear(bool $attachments): bool
348 - {
349 - $this->_message->headerinfo = $this->mailbox->getParsedHeaders($this->_message->messageNB);
350 - if (empty($this->_message->headerinfo['subject'])) {
351 - if (empty($this->_message->headerinfo['Subject'])) {
352 - return false;
353 - }
354 - $this->_message->headerinfo['subject'] = $this->_message->headerinfo['Subject'];
355 - }
356 - $this->_message->text = '';
357 - $this->_message->html = $this->mailbox->getBody($this->_message->messageNB);
358 -
359 - if (!empty($this->_message->headerinfo['content-type']) && strpos($this->_message->headerinfo['content-type'], 'boundary') !== false) {
360 - $matches = [];
361 - preg_match('#boundary="([^"]+)"#i', $this->_message->headerinfo['content-type'], $matches);
362 -
363 - if (!empty($matches[1]) && strpos($this->_message->html, $matches[1]) !== false) {
364 - $inlineImages = [];
365 -
366 - $segments = explode('--'.$matches[1], $this->_message->html);
367 - foreach ($segments as $segment) {
368 - if (strpos($segment, 'Content-Type: text/plain') !== false) {
369 - $matches = [];
370 - preg_match('#boundary="([^"]+)"#i', $segment, $matches);
371 -
372 - if (!empty($matches[1])) {
373 - $parts = explode('--'.$matches[1], $segment);
374 -
375 - foreach ($parts as $onePart) {
376 - $content = trim(preg_replace('#(charset=".*?\r\n)|Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n#is', "", $onePart));
377 -
378 - if (strpos($onePart, 'Content-Transfer-Encoding') !== false) {
379 - preg_match('#Content-Transfer-Encoding: (.+)#i', $onePart, $encoding);
380 - $encoding = trim($encoding[1]);
381 - if (!empty($encoding) && $encoding !== 'Content-Type:') {
382 - $content = mb_convert_encoding($content, 'UTF-8', $encoding);
383 - }
384 - }
385 -
386 - if (strpos($onePart, "Content-Type: text/plain") !== false) {
387 - $this->_message->text .= ' '.$content;
388 - }
389 - if (strpos($onePart, "Content-Type: text/html") !== false) {
390 - $this->_message->html .= ' '.$content;
391 - }
392 - }
393 - } else {
394 - $this->_message->html .= ' '.trim(preg_replace('#(charset=".*?\r\n)|Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n#is', "", $segment));
395 - }
396 - } elseif (preg_match("#Content-Type: .*?/(png|jpg|jpeg|gif)#i", $segment) !== false) {
397 - preg_match('#name="([^"]+)"#i', $segment, $filename);
398 - if (empty($filename) || empty($filename[1])) {
399 - continue;
400 - }
401 - $name = trim($filename[1]);
402 - $filename = $name;
403 - $extensionPos = strrpos($filename, '.');
404 -
405 - if ($extensionPos === false) {
406 - continue;
407 - }
408 -
409 - $extension = substr($filename, $extensionPos + 1);
410 -
411 - $filename = preg_replace('#[^a-zA-Z0-9]#Uis', '_', substr($filename, 0, $extensionPos));
412 -
413 - $uploadFolder = str_replace(['/', '\\'], DS, acym_getFilesFolder());
414 - $pathToUpload = ACYM_ROOT.trim($uploadFolder, DS).DS;
415 -
416 - if (file_exists($pathToUpload.$filename.'.'.$extension)) {
417 - $fileNumber = 1;
418 - while (file_exists($pathToUpload.$filename.'_('.$fileNumber.').'.$extension)) {
419 - $fileNumber++;
420 - }
421 - $filename = $filename.'_('.$fileNumber.')';
422 - }
423 -
424 - if ($attachments && preg_match("#Content-Disposition: attachment;#i", $segment) !== false) {
425 - $data = trim(substr($segment, strpos($segment, "\r\n\r\n")));
426 - if (strpos($segment, 'Content-Transfer-Encoding: base64') !== false) {
427 - $data = base64_decode($data);
428 - }
429 -
430 - try {
431 - if (acym_writeFile($pathToUpload.$filename.'.'.$extension, $data)) {
432 - $attachment = new \stdClass();
433 - $attachment->filename = str_replace(DS, '/', trim($uploadFolder, '\\/')).'/'.$filename.'.'.$extension;
434 - $attachment->size = filesize(ACYM_ROOT.$attachment->filename);
435 - $this->attachments[] = $attachment;
436 - }
437 - } catch (\Exception $e) {
438 - $completeFilename = $filename.'.'.$extension;
439 - $this->display(acym_translationSprintf('ACYM_ERROR_UPLOADING_ATTACHMENT_X_TO_X', $completeFilename, $e->getMessage()), self::MESSAGE_TYPE_ERROR);
440 - }
441 - } else {
442 - preg_match('#Content-ID: <([^>]+)>#i', $segment, $contentID);
443 - if (empty($contentID) || empty($contentID[1])) {
444 - continue;
445 - }
446 - $contentID = trim($contentID[1]);
447 -
448 - $data = trim(substr($segment, strpos($segment, "\r\n\r\n")));
449 - if (strpos($segment, 'Content-Transfer-Encoding: base64') !== false) {
450 - $data = base64_decode($data);
451 - }
452 -
453 -
454 - try {
455 - if (acym_writeFile($pathToUpload.$filename.'.'.$extension, $data)) {
456 - $inlineImages['cid:'.$contentID] = acym_rootURI().$uploadFolder.'/'.$filename.'.'.$extension;
457 - }
458 - } catch (\Exception $e) {
459 - $this->display(acym_translationSprintf('ACYM_ERROR_UPLOAD_ATTACHMENT', $filename.'.'.$extension, $e->getMessage()), self::MESSAGE_TYPE_ERROR);
460 - }
461 - }
462 - }
463 - }
464 -
465 - if (!empty($inlineImages)) {
466 - $this->_message->html = str_replace(array_keys($inlineImages), $inlineImages, $this->_message->html);
467 - }
468 - }
469 - }
470 -
471 - $this->_message->subject = $this->decodeHeader($this->_message->headerinfo['subject']);
472 - if (empty($this->_message->header)) {
473 - $this->_message->header = new \stdClass();
474 - }
475 - $this->_message->header->sender_email = @$this->_message->headerinfo['return-path'];
476 - if (is_array($this->_message->header->sender_email)) {
477 - $this->_message->header->sender_email = reset($this->_message->header->sender_email);
478 - }
479 - if (!empty($this->_message->header->sender_email) && preg_match($this->detectEmail, $this->_message->header->sender_email, $results)) {
480 - $this->_message->header->sender_email = $results[0];
481 - }
482 - $this->_message->header->sender_name = strip_tags(@$this->_message->headerinfo['from'] ?? '');
483 - $this->_message->header->reply_to_email = $this->_message->header->sender_email;
484 - $this->_message->header->reply_to_name = $this->_message->header->sender_name;
485 - $this->_message->header->from_email = $this->_message->header->sender_email;
486 - $this->_message->header->from_name = $this->_message->header->sender_name;
487 -
488 - return true;
489 - }
490 -
491 - private function decodeMessageImap(bool $attachments): bool
492 - {
493 - $this->_message->structure = $this->callImapFunction('imap_fetchstructure', [$this->mailbox, $this->_message->messageNB]);
494 -
495 - if (empty($this->_message->structure)) {
496 - return false;
497 - }
498 -
499 - $this->_message->headerinfo = $this->callImapFunction('imap_fetchheader', [$this->mailbox, $this->_message->messageNB]);
500 -
501 - $this->_message->html = '';
502 - $this->_message->text = '';
503 -
504 - if ($this->_message->structure->type == 1) {
505 - $this->_message->contentType = 2;
506 - if ($this->_message->structure->subtype === 'MIXED') {
507 - $allParts = $this->explodeBodyMixed($this->_message->structure);
508 - } else {
509 - $allParts = $this->explodeBody($this->_message->structure);
510 - }
511 -
512 - $text = '';
513 - foreach ($allParts as $num => $onePart) {
514 - $content = $this->callImapFunction('imap_fetchbody', [$this->mailbox, $this->_message->messageNB, $num]);
515 - $decodedContent = empty($content) ? '' : $this->decodeContent($content, $onePart);
516 - if ($onePart->subtype === 'HTML') {
517 - $this->_message->html .= $decodedContent;
518 - } else {
519 - if ($onePart->subtype === 'PLAIN') {
520 - $text .= $decodedContent."\n";
521 - }
522 - $this->_message->text .= $decodedContent."\n\n- - -\n\n";
523 - }
524 -
525 - if (!empty($this->action) && $attachments) {
526 - if ((isset($onePart->parameters) && is_array($onePart->parameters)) || (isset($onePart->dparameters) && is_array($onePart->dparameters))) {
527 - $this->uploadAttachment($onePart, $num);
528 - } elseif (isset($onePart->parts) && is_array($onePart->parts)) {
529 - foreach ($onePart->parts as $num2 => $innerPart) {
530 - $this->uploadAttachment($innerPart, $num2);
531 - }
532 - }
533 - }
534 - }
535 -
536 - if (!empty($this->action) && empty($this->_message->html)) {
537 - $this->_message->html = $text."\n";
538 - }
539 - if (!empty($this->inlineImages)) {
540 - $this->_message->html = str_replace(array_keys($this->inlineImages), $this->inlineImages, $this->_message->html);
541 - }
542 - } else {
543 - if ($this->_message->structure->subtype === 'HTML') {
544 - $this->_message->contentType = 1;
545 - $this->_message->html = $this->decodeContent($this->callImapFunction('imap_body', [$this->mailbox, $this->_message->messageNB]), $this->_message->structure);
546 - } else {
547 - $this->_message->contentType = 0;
548 - $this->_message->text = $this->decodeContent($this->callImapFunction('imap_body', [$this->mailbox, $this->_message->messageNB]), $this->_message->structure);
549 - }
550 - }
551 -
552 - if (!empty($this->_message->subject)) {
553 - $this->_message->subject = $this->decodeHeader($this->_message->subject);
554 - } else {
555 - $this->_message->subject = '';
556 - }
557 -
558 - $this->decodeAddressImap('sender');
559 - $this->decodeAddressImap('from');
560 - $this->decodeAddressImap('reply_to');
561 - $this->decodeAddressImap('to');
562 -
563 - return true;
564 - }
565 -
566 - private function uploadAttachment($attachment, $num): void
567 - {
568 - if (!isset($attachment->disposition) || (strtolower($attachment->disposition) !== 'attachment' && strtolower($attachment->disposition) !== 'inline')) {
569 - return;
570 - }
571 -
572 - $name = empty($attachment->dparameters[0]->value) ? $attachment->parameters[0]->value : $attachment->dparameters[0]->value;
573 - $filename = $this->decodeHeader($name);
574 - $extensionPos = strrpos($filename, '.');
575 -
576 - if ($extensionPos === false) {
577 - foreach ($attachment->parameters as $key => $oneParam) {
578 - if (strtolower($oneParam->attribute) === 'name') {
579 - $filename = $this->decodeHeader($attachment->parameters[$key]->value);
580 - $extensionPos = strrpos($filename, '.');
581 - break;
582 - }
583 - }
584 - }
585 -
586 - if ($extensionPos === false) {
587 - return;
588 - }
589 -
590 - $extension = substr($filename, $extensionPos + 1);
591 - if (!in_array(strtolower($extension), $this->allowed_extensions)) {
592 - return;
593 - }
594 -
595 - $filename = preg_replace('#[^a-zA-Z0-9]#Uis', '_', substr($filename, 0, $extensionPos));
596 -
597 - $uploadFolder = str_replace(['/', '\\'], DS, acym_getFilesFolder());
598 - $pathToUpload = ACYM_ROOT.trim($uploadFolder, DS).DS;
599 -
600 - if (file_exists($pathToUpload.$filename.'.'.$extension)) {
601 - $fileNumber = 1;
602 - while (file_exists($pathToUpload.$filename.'_('.$fileNumber.').'.$extension)) {
603 - $fileNumber++;
604 - }
605 - $filename = $filename.'_('.$fileNumber.')';
606 - }
607 -
608 - $data = $this->decodeContent($this->callImapFunction('imap_fetchbody', [$this->mailbox, $this->_message->messageNB, $num]), $attachment);
609 -
610 - try {
611 - if (acym_writeFile($pathToUpload.$filename.'.'.$extension, $data)) {
612 - if (
613 - strtolower($attachment->disposition) !== 'attachment'
614 - && !empty($attachment->id)
615 - && in_array(strtolower($attachment->subtype), acym_getImageFileExtensions())
616 - ) {
617 - $this->inlineImages['cid:'.trim($attachment->id, '<>')] = acym_rootURI().$uploadFolder.'/'.$filename.'.'.$extension;
618 - } else {
619 - $attachment = new \stdClass();
620 - $attachment->filename = str_replace(DS, '/', trim($uploadFolder, '\\/')).'/'.$filename.'.'.$extension;
621 - $attachment->size = filesize(ACYM_ROOT.$attachment->filename);
622 - $this->attachments[] = $attachment;
623 - }
624 - }
625 - } catch (\Exception $e) {
626 - $completeFilename = $filename.'.'.$extension;
627 - $this->display(acym_translationSprintf('ACYM_ERROR_UPLOADING_ATTACHMENT_X_TO_X', $completeFilename, $e->getMessage()), self::MESSAGE_TYPE_ERROR);
628 - }
629 - }
630 -
631 - public function handleMessages(): void
632 - {
633 - $maxMessages = min($this->nbMessages, $this->config->get('bounce_max', 0));
634 - if (empty($maxMessages)) {
635 - $maxMessages = min($this->nbMessages, 500);
636 - }
637 -
638 - if ($this->report) {
639 - if (function_exists('apache_get_modules')) {
640 - $modules = apache_get_modules();
641 - $this->mod_security2 = in_array('mod_security2', $modules);
642 - }
643 -
644 - @ini_set('output_buffering', 'off');
645 - @ini_set('zlib.output_compression', 0);
646 -
647 -
648 - if (!headers_sent()) {
649 - while (ob_get_level() > 0 && $this->obend++ < 3) {
650 - @ob_end_flush();
651 - }
652 - }
653 -
654 - $disp = "<div style='position:fixed; top:3px;left:3px;background-color : white;border : 1px solid grey; padding : 3px;font-size:14px'>";
655 - $disp .= acym_translation('ACYM_BOUNCE_HANDLING');
656 - $disp .= ': <span id="counter">0</span> / '.$maxMessages;
657 - $disp .= '</div>';
658 - $disp .= '<script type="text/javascript" language="javascript">';
659 - $disp .= 'var mycounter = document.getElementById("counter");';
660 - $disp .= 'function setCounter(val){ mycounter.innerHTML=val;}';
661 - $disp .= '</script>';
662 - echo $disp;
663 - if (function_exists('ob_flush')) {
664 - @ob_flush();
665 - }
666 - if (!$this->mod_security2) {
667 - @flush();
668 - }
669 - }
670 -
671 - $rules = $this->ruleClass->getAll();
672 - $rules = array_filter($rules, function ($rule) {
673 - return $rule->active;
674 - });
675 -
676 - $msgNB = $maxMessages;
677 - $listClass = new ListClass();
678 - $this->allLists = $listClass->getAll('id');
679 -
680 - $replyemail = $this->config->get('reply_email');
681 - $fromemail = $this->config->get('from_email');
682 - $bouncemail = $this->config->get('bounce_email');
683 - $removeEmails = '#('.str_replace(['%'], ['@'], $this->config->get('bounce_username'));
684 - if (!empty($bouncemail)) {
685 - $removeEmails .= '|'.$bouncemail;
686 - }
687 - if (!empty($fromemail)) {
688 - $removeEmails .= '|'.$fromemail;
689 - }
690 - if (!empty($replyemail)) {
691 - $removeEmails .= '|'.$replyemail;
692 - }
693 - $removeEmails .= ')#i';
694 -
695 - while (($msgNB > 0) && ($this->_message = $this->getMessage($msgNB))) {
696 - if ($this->report) {
697 - echo '<script type="text/javascript" language="javascript">setCounter('.($maxMessages - $msgNB + 1).')</script>';
698 - if (function_exists('ob_flush')) {
699 - @ob_flush();
700 - }
701 - if (!$this->mod_security2) {
702 - @flush();
703 - }
704 - }
705 - $this->_message->messageNB = $msgNB;
706 - $msgNB--;
707 -
708 - if (!$this->decodeMessage()) {
709 - $this->display(acym_translation('ACYM_ERROR_RETRIEVING_MESSAGE'), self::MESSAGE_TYPE_ERROR, $maxMessages - $this->_message->messageNB + 1);
710 - continue;
711 - }
712 -
713 - if (empty($this->_message->subject)) {
714 - $this->_message->subject = '';
715 - }
716 -
717 - $this->_message->analyseText = $this->_message->html.' '.$this->_message->text;
718 - if (!empty($this->_message->header->from_email)) {
719 - $this->_message->analyseText .= ' '.$this->_message->header->from_email;
720 - }
721 - $this->display(
722 - '<b>'.acym_translation('ACYM_EMAIL_SUBJECT').' : '.strip_tags($this->_message->subject).'</b>',
723 - self::MESSAGE_TYPE_SUCCESS,
724 - $maxMessages - $this->_message->messageNB + 1
725 - );
726 -
727 - preg_match('#AC([0-9]+)Y([0-9]+)BA#i', $this->_message->analyseText, $resultsVars);
728 - if (!empty($resultsVars[1])) {
729 - $this->_message->userid = $resultsVars[1];
730 - }
731 - if (!empty($resultsVars[2])) {
732 - $this->_message->mailid = $resultsVars[2];
733 - }
734 -
735 - if (empty($this->_message->userid)) {
736 - preg_match_all($this->detectEmail, $this->_message->analyseText, $results);
737 -
738 - if (empty($results[0])) {
739 - preg_match_all($this->detectEmail2, $this->_message->analyseText, $results2);
740 - for ($i = 0; $i < count($results2[0]); $i++) {
741 - $results[0][] = $results2[3][$i].'@'.$results2[1][$i];
742 - }
743 - }
744 -
745 - if (!empty($results[0])) {
746 - $alreadyChecked = [];
747 - foreach ($results[0] as $oneEmail) {
748 - if (!preg_match($removeEmails, $oneEmail)) {
749 - $this->_message->subemail = strtolower($oneEmail);
750 - if (!empty($alreadyChecked[$this->_message->subemail])) {
751 - continue;
752 - }
753 -
754 - $user = $this->userClass->getOneByEmail($this->_message->subemail);
755 - if (!empty($user)) $this->_message->userid = $user->id;
756 -
757 - if (empty($this->_message->userid) && preg_match('#(&|\?)id=([0-9]+)&#ims', $this->_message->subemail, $subid)) {
758 - $user = $this->userClass->getOneById($subid[2]);
759 - $this->_message->userid = $user->id;
760 - $this->_message->subemail = $user->email;
761 - }
762 -
763 - $alreadyChecked[$this->_message->subemail] = true;
764 - if (!empty($this->_message->userid)) {
765 - break;
766 - }
767 - }
768 - }
769 - }
770 - }
771 -
772 - if (empty($this->_message->mailid) && !empty($this->_message->userid)) {
773 - $this->_message->mailid = acym_loadResult(
774 - 'SELECT `mail_id` FROM #__acym_user_stat WHERE `user_id` = '.intval($this->_message->userid).' ORDER BY `send_date` DESC'
775 - );
776 - }
777 -
778 - foreach ($rules as $oneRule) {
779 - if ($this->handleRule($oneRule)) {
780 - break;
781 - }
782 - }
783 -
784 - if ($msgNB % 50 == 0) {
785 - $this->userActions();
786 - }
787 -
788 - if (!empty($this->stoptime) && time() > $this->stoptime) {
789 - break;
790 - }
791 - }
792 -
793 - $this->userActions();
794 - $this->close();
795 - }
796 -
797 - private function userActions(): void
798 - {
799 - if (!empty($this->deletedUsers)) {
800 - acym_arrayToInteger($this->deletedUsers);
801 - $this->userClass->delete($this->deletedUsers);
802 - $this->deletedUsers = [];
803 - }
804 -
805 - if (!empty($this->blockedUsers)) {
806 - acym_arrayToInteger($this->blockedUsers);
807 - $this->userClass->deactivate($this->blockedUsers);
808 - $allUsersId = implode(',', $this->blockedUsers);
809 - acym_query('DELETE FROM `#__acym_queue` WHERE `user_id` IN ('.$allUsersId.')');
810 - $this->blockedUsers = [];
811 - }
812 -
813 - if (!empty($this->bounceMessages)) {
814 - foreach ($this->bounceMessages as $mailid => $bouncedata) {
815 -
816 - $updateBounceDetails = '';
817 - if (!empty($bouncedata['bouncedetails'])) {
818 - $bouncedetails = acym_loadResult('SELECT `bounce_details` FROM #__acym_mail_stat WHERE mail_id = '.intval($mailid));
819 - if (!empty($bouncedetails)) {
820 - $bouncedetails = unserialize($bouncedetails);
821 - } else {
822 - $bouncedetails = [];
823 - }
824 -
825 - foreach ($bouncedata['bouncedetails'] as $ruleName => $nbTimes) {
826 - if (empty($bouncedetails[$ruleName])) {
827 - $bouncedetails[$ruleName] = 0;
828 - }
829 - $bouncedetails[$ruleName] += $nbTimes;
830 - }
831 -
832 - $updateBounceDetails = ' , `bounce_details` = '.acym_escapeDB(serialize($bouncedetails));
833 - }
834 -
835 - if (!empty($bouncedata['userids'])) {
836 - $valueInsert = [];
837 - foreach ($bouncedata['ruletriggered'] as $userid => $rulename) {
838 - $valueInsert[] = '('.intval($mailid).','.intval($userid).',1,'.acym_escapeDB($rulename).')';
839 - }
840 - acym_query(
841 - 'INSERT INTO #__acym_user_stat (mail_id,user_id,bounce,bounce_rule) VALUES '.implode(
842 - ',',
843 - $valueInsert
844 - ).' ON DUPLICATE KEY UPDATE `bounce` = `bounce` + 1, bounce_rule=VALUES(bounce_rule)'
845 - );
846 - acym_arrayToInteger($bouncedata['userids']);
847 - $realUniqueBounces = acym_loadResult(
848 - 'SELECT COUNT(*)
849 - FROM #__acym_user_stat
850 - WHERE `bounce` = 1
851 - AND `user_id` IN ('.implode(',', $bouncedata['userids']).')
852 - AND `mail_id` = '.intval($mailid)
853 - );
854 - $bouncedata['nbbounces'] = $bouncedata['nbbounces'] - count($bouncedata['userids']) + $realUniqueBounces;
855 - }
856 -
857 - acym_query(
858 - 'UPDATE #__acym_mail_stat SET `bounce_unique` = `bounce_unique` + '.intval($bouncedata['nbbounces']).$updateBounceDetails.'
859 - WHERE `mail_id` = '.intval($mailid).'
860 - LIMIT 1'
861 - );
862 - }
863 - $this->bounceMessages = [];
864 - }
865 - }
866 -
867 - private function handleRule(object &$oneRule): bool
868 - {
869 - $regex = $oneRule->regex;
870 - if (empty($regex)) {
871 - return false;
872 - }
873 -
874 -
875 - $analyseText = '';
876 - if (in_array('senderInfo', $oneRule->executed_on)) {
877 - $analyseText .= ' ';
878 - if (isset($this->_message->header->sender_name)) {
879 - $analyseText .= $this->_message->header->sender_name;
880 - }
881 - if (isset($this->_message->header->sender_email)) {
882 - $analyseText .= $this->_message->header->sender_email;
883 - }
884 - }
885 -
886 - if (in_array('subject', $oneRule->executed_on)) {
887 - $analyseText .= ' '.$this->_message->subject;
888 - }
889 -
890 - if (in_array('body', $oneRule->executed_on)) {
891 - if (!empty($this->_message->html)) {
892 - $analyseText .= ' '.$this->_message->html;
893 - }
894 - if (!empty($this->_message->text)) {
895 - $analyseText .= ' '.$this->_message->text;
896 - }
897 - }
898 -
899 - $analyseText = str_replace(["\n", "\r", "\t"], ' ', $analyseText);
900 -
901 - if (!preg_match('#'.$regex.'#ims', $analyseText)) {
902 - return false;
903 - }
904 -
905 - $message = acym_translation('ACYM_BOUNCE_RULE').' ['.acym_translation('ACYM_ID').' '.$oneRule->id.'] '.acym_translation($oneRule->name).' : ';
906 - $this->errorDuringActions = false;
907 - $message .= $this->actionUser($oneRule);
908 - $message .= $this->actionMessage($oneRule);
909 -
910 - $type = self::MESSAGE_TYPE_SUCCESS;
911 - if ($this->errorDuringActions) {
912 - $type = self::MESSAGE_TYPE_ERROR;
913 - }
914 -
915 - $this->display($message, $type);
916 -
917 - return true;
918 - }
919 -
920 - private function actionUser(object &$oneRule): string
921 - {
922 - $commonListsMailUser = [];
923 - $message = '';
924 -
925 - if (empty($this->_message->userid)) {
926 - $message .= 'user not identified';
927 - if (!empty($this->_message->subemail)) {
928 - $message .= ' ( '.$this->_message->subemail.' ) ';
929 - }
930 -
931 - return $message;
932 - }
933 -
934 - if (
935 - in_array('delete_user_subscription', $oneRule->action_user)
936 - || in_array('unsubscribe_user', $oneRule->action_user)
937 - || in_array('subscribe_user', $oneRule->action_user)
938 - ) {
939 - if (empty($this->_message->subemail)) {
940 - $currentUser = $this->userClass->getOneById($this->_message->userid);
941 - if (!empty($currentUser->email)) {
942 - $this->_message->subemail = $currentUser->email;
943 - }
944 - }
945 -
946 - $userSubscription = $this->userClass->getSubscriptionStatus($this->_message->userid);
947 -
948 - $bounceContent = '';
949 - if (!empty($this->_message->html)) {
950 - $bounceContent .= ' '.$this->_message->html;
951 - }
952 - if (!empty($this->_message->text)) {
953 - $bounceContent .= ' '.$this->_message->text;
954 - }
955 -
956 - $emailLists = [];
957 - preg_match('#Please unsubscribe user ID \d+ from list\(s\) ([^.]+)\.#Uis', $bounceContent, $emailLists);
958 - if (!empty($emailLists[1])) {
959 - $currentEmailLists = explode(',', $emailLists[1]);
960 - foreach ($currentEmailLists as $oneListId) {
961 - if (!empty($userSubscription[$oneListId]) && $userSubscription[$oneListId]->status == 1) {
962 - $commonListsMailUser[] = $oneListId;
963 - }
964 - }
965 - }
966 -
967 - if (empty($commonListsMailUser)) {
968 - $commonListsMailUser = array_keys($userSubscription);
969 - }
970 - }
971 -
972 - if (empty($this->_message->subemail)) {
973 - $this->_message->subemail = $this->_message->userid;
974 - }
975 -
976 -
977 - if (!empty($this->_message->mailid)) {
978 - $mail = $this->mailClass->getOneById($this->_message->mailid);
979 - } else {
980 - $mail = null;
981 - }
982 -
983 - if ($oneRule->increment_stats && !empty($this->_message->mailid) && !empty($mail)) {
984 - if (empty($this->bounceMessages[$this->_message->mailid])) {
985 - $this->bounceMessages[$this->_message->mailid] = [];
986 - $this->bounceMessages[$this->_message->mailid]['nbbounces'] = 0;
987 - $this->bounceMessages[$this->_message->mailid]['userids'] = [];
988 - $this->bounceMessages[$this->_message->mailid]['bouncedetails'] = [];
989 - $this->bounceMessages[$this->_message->mailid]['ruletriggered'] = [];
990 - }
991 -
992 - $this->bounceMessages[$this->_message->mailid]['nbbounces']++;
993 -
994 - $ruleName = $oneRule->name.' [ID '.$oneRule->id.'] ';
995 - if (empty($this->bounceMessages[$this->_message->mailid]['bouncedetails'][$ruleName])) {
996 - $this->bounceMessages[$this->_message->mailid]['bouncedetails'][$ruleName] = 1;
997 - } else {
998 - $this->bounceMessages[$this->_message->mailid]['bouncedetails'][$ruleName]++;
999 - }
1000 -
1001 - $user = $this->userClass->getOneById($this->_message->userid);
1002 - if (!empty($this->_message->userid) && !in_array('delete_user', $oneRule->action_user) && !empty($user)) {
1003 - $this->bounceMessages[$this->_message->mailid]['userids'][] = intval($this->_message->userid);
1004 - $this->bounceMessages[$this->_message->mailid]['ruletriggered'][intval($this->_message->userid)] = $oneRule->name.' ['.acym_translation(
1005 - 'ACYM_ID'
1006 - ).' '.$oneRule->id.']';
1007 - }
1008 - }
1009 -
1010 - if (!empty($oneRule->execute_action_after) && $oneRule->execute_action_after > 1) {
1011 - if (empty($this->_message->mailid)) {
1012 - $this->_message->mailid = 0;
1013 - }
1014 - $nb = intval(
1015 - acym_loadResult(
1016 - 'SELECT COUNT(mail_id) FROM #__acym_user_stat WHERE bounce > 0 AND user_id = '.intval($this->_message->userid).' AND mail_id != '.intval(
1017 - $this->_message->mailid
1018 - )
1019 - )
1020 - ) + 1;
1021 -
1022 - if ($nb < $oneRule->execute_action_after) {
1023 - $message .= ' | '.acym_translationSprintf('ACYM_BOUNCE_RECEIVED', $nb, $this->_message->subemail).' | '.acym_translationSprintf(
1024 - 'ACYM_BOUNCE_MIN_EXEC',
1025 - $oneRule->execute_action_after
1026 - );
1027 -
1028 - return $message;
1029 - }
1030 - }
1031 -
1032 - if (in_array('delete_user', $oneRule->action_user)) {
1033 - $message .= ' | '.acym_translationSprintf('ACYM_USER_X_DELETED', $this->_message->subemail);
1034 - $this->deletedUsers[] = intval($this->_message->userid);
1035 -
1036 - return $message;
1037 - }
1038 -
1039 - $listId = 0;
1040 - if (in_array('subscribe_user', $oneRule->action_user) && !empty($oneRule->action_user['subscribe_user_list'])) {
1041 - $listId = $oneRule->action_user['subscribe_user_list'];
1042 -
1043 - $listName = empty($this->allLists[$listId]->name) ? $listId : $this->allLists[$listId]->name;
1044 - $message .= ' | ';
1045 - if (isset($userSubscription[$listId])) {
1046 - $message .= acym_translationSprintf('ACYM_USER_X_NOT_SUBSCRIBED_TO', $this->_message->subemail, $listName);
1047 - if ($userSubscription[$listId]->status == 1) {
1048 - $message .= acym_translation('ACYM_USER_ALREADY_SUBSCRIBED');
1049 - } elseif ($userSubscription[$listId]->status == 0) {
1050 - $message .= acym_translation('ACYM_USER_ALREADY_UNSUBSCRIBED');
1051 - }
1052 - } else {
1053 - $this->userClass->subscribe([$this->_message->userid], [$listId]);
1054 - $message .= acym_translationSprintf('ACYM_USER_X_SUBSCRIBED_TO', $this->_message->subemail, $listName);
1055 - }
1056 - }
1057 -
1058 - if (in_array('delete_user_subscription', $oneRule->action_user)) {
1059 - $removeLists = array_diff($commonListsMailUser, [$listId]);
1060 - if (!empty($removeLists)) {
1061 - $listNames = [];
1062 - foreach ($removeLists as $oneListId) {
1063 - if (!empty($this->allLists[$oneListId]->name)) {
1064 - $listNames[] = $this->allLists[$oneListId]->name;
1065 - } else {
1066 - $listNames[] = $oneListId;
1067 - }
1068 - }
1069 - $message .= ' | '.acym_translationSprintf('ACYM_USER_X_REMOVED_FROM', $this->_message->subemail, implode(', ', $listNames));
1070 - $this->userClass->removeSubscription([$this->_message->userid], $removeLists);
1071 - } else {
1072 - $message .= ' | '.acym_translationSprintf('ACYM_USER_X_NOT_SUBSCRIBED', $this->_message->subemail);
1073 - }
1074 - }
1075 -
1076 - if (in_array('unsubscribe_user', $oneRule->action_user)) {
1077 - $unsubLists = array_diff($commonListsMailUser, [$listId]);
1078 - if (!empty($unsubLists)) {
1079 - $listNames = [];
1080 - foreach ($unsubLists as $oneListId) {
1081 - if (!empty($this->allLists[$oneListId]->name)) {
1082 - $listNames[] = $this->allLists[$oneListId]->name;
1083 - } else {
1084 - $listNames[] = $oneListId;
1085 - }
1086 - }
1087 - $this->userClass->unsubscribe([$this->_message->userid], $unsubLists);
1088 - $message .= ' | '.acym_translationSprintf('ACYM_USER_X_UNSUBSCRIBED_FROM', $this->_message->subemail, implode(', ', $listNames));
1089 - } else {
1090 - $message .= ' | '.acym_translationSprintf('ACYM_USER_X_NOT_SUBSCRIBED', $this->_message->subemail);
1091 - }
1092 - }
1093 -
1094 - if (in_array('block_user', $oneRule->action_user)) {
1095 - $message .= ' | '.acym_translationSprintf('ACYM_USER_X_BLOCKED', $this->_message->subemail.' ( '.acym_translation('ACYM_ID').' '.intval($this->_message->userid).' )');
1096 - $this->blockedUsers[] = intval($this->_message->userid);
1097 - }
1098 -
1099 - if (in_array('empty_queue_user', $oneRule->action_user)) {
1100 - $affected = acym_query('DELETE FROM #__acym_queue WHERE user_id = '.intval($this->_message->userid));
1101 - $message .= ' | '.acym_translationSprintf(
1102 - 'ACYM_USER_X_QUEUE',
1103 - $this->_message->subemail.' ( '.acym_translation('ACYM_ID').' '.intval($this->_message->userid).' )',
1104 - acym_translationSprintf('ACYM_SUCC_DELETE_ELEMENTS', $affected)
1105 - );
1106 - }
1107 -
1108 - return $message;
1109 - }
1110 -
1111 - private function actionMessage(object &$oneRule): string
1112 - {
1113 - $message = '';
1114 -
1115 - if (!empty($oneRule->action_message['forward_to'])) {
1116 - if (
1117 - strtolower($oneRule->action_message['forward_to']) === strtolower($this->config->get('bounce_username'))
1118 - || strtolower($oneRule->action_message['forward_to']) === strtolower($this->config->get('bounce_email'))
1119 - ) {
1120 - $oneRule->action_message['forward_to'] = '';
1121 - unset($oneRule->action_message['delete_message']);
1122 - $message .= ' | '.acym_translation('ACYM_BOUNCE_NOT_FORWARD');
1123 - }
1124 - }
1125 -
1126 -
1127 - $donotdelete = false;
1128 -
1129 - if (in_array('save_message', $oneRule->action_message) && !empty($this->_message->userid) && !in_array('delete_user', $oneRule->action_user)) {
1130 - $data = [];
1131 - $data[] = 'SUBJECT::'.@htmlentities($this->_message->subject, ENT_COMPAT, 'UTF-8');
1132 - $data[] = 'ACY_RULE::'.$oneRule->id.' '.$oneRule->name;
1133 - $data[] = 'REPLYTO_ADDRESS::'.$this->_message->header->reply_to_name.' ( '.$this->_message->header->reply_to_email.' )';
1134 - $data[] = 'FROM_ADDRESS::'.$this->_message->header->from_name.' ( '.$this->_message->header->from_email.' )';
1135 - $data[] = @htmlentities(print_r($this->_message->headerinfo, true), ENT_COMPAT, 'UTF-8');
1136 - if (empty($this->_message->mailid)) {
1137 - $this->_message->mailid = 0;
1138 - }
1139 - try {
1140 - $this->historyClass->insert($this->_message->userid, 'bounce', $data, $this->_message->mailid);
1141 - $message .= ' | '.acym_translationSprintf('ACYM_BOUNCE_MESSAGE_SAVED', $this->_message->userid);
1142 - } catch (\Exception $e) {
1143 - $message .= ' | '.acym_translationSprintf(
1144 - 'ACYM_BOUNCE_SAVE_ERROR_USER_MAIL_RULE',
1145 - $this->_message->userid,
1146 - $this->_message->mailid,
1147 - $oneRule->id,
1148 - $e->getMessage()
1149 - );
1150 - $donotdelete = true;
1151 - }
1152 - }
1153 -
1154 -
1155 - if (!empty($oneRule->action_message['forward_to'])) {
1156 - $this->mailer->clearAll();
1157 - $this->mailer->Subject = 'BOUNCE FORWARD : '.$this->_message->subject;
1158 -
1159 - if (substr_count($oneRule->action_message['forward_to'], '@') > 1) {
1160 - $forwardAddresses = explode(';', str_replace([';', ','], ';', $oneRule->action_message['forward_to']));
1161 - } else {
1162 - $forwardAddresses = [$oneRule->action_message['forward_to']];
1163 - }
1164 -
1165 - foreach ($forwardAddresses as $oneForwardAddress) {
1166 - $this->mailer->AddAddress($this->mailer->cleanText($oneForwardAddress));
1167 - }
1168 -
1169 - $info = acym_translation('ACYM_BOUNCE_RULE').' ['.acym_translation('ACYM_ID').' '.$oneRule->id.'] '.acym_translation($oneRule->name);
1170 - if (!empty($this->_message->html)) {
1171 - $this->mailer->isHTML();
1172 - if (!empty($this->_message->userid)) {
1173 - $info .= ' ('.acym_translation('ACYM_USER_ID').': '.$this->_message->userid.')';
1174 - } elseif (!empty($this->_message->subemail)) {
1175 - $info .= ' ('.acym_translation('ACYM_USER_EMAIL').': '.$this->_message->subemail.')';
1176 - }
1177 -
1178 - $this->mailer->Body = $info.'<br />'.$this->_message->html;
1179 - if (!empty($this->_message->text)) {
1180 - $this->mailer->Body .= '<br /><br />-------<br />'.nl2br($this->_message->text);
1181 - }
1182 - } else {
1183 - $this->mailer->isHTML(false);
1184 - $this->mailer->Body = $info."\n".$this->_message->text;
1185 - }
1186 -
1187 - $this->mailer->Body .= print_r($this->_message->headerinfo, true);
1188 - $replyAddress = trim(@$this->_message->header->reply_to_email, '<> ');
1189 - if (!empty($replyAddress)) {
1190 - if ($this->config->get('mailer_method') === \plgAcymAcymailer::SENDING_METHOD_ID) {
1191 - $this->mailer->AddReplyTo($this->config->get('replyto_email'), $this->config->get('replyto_name'));
1192 - } else {
1193 - $this->mailer->AddReplyTo(trim($this->_message->header->reply_to_email, '<> '), $this->_message->header->reply_to_name);
1194 - }
1195 - }
1196 -
1197 - $this->mailer->isForward = true;
1198 - if ($this->mailer->send()) {
1199 - $message .= ' | '.acym_translationSprintf('ACYM_FORWARDED_TO_X', $oneRule->action_message['forward_to']);
1200 - } else {
1201 - $message .= ' | '.acym_translationSprintf('ACYM_NOT_FORWARDED_TO_X', $oneRule->action_message['forward_to'], $this->mailer->reportMessage);
1202 - $this->errorDuringActions = true;
1203 - }
1204 - }
1205 -
1206 - if (in_array('delete_message', $oneRule->action_message) && !$this->errorDuringActions) {
1207 - $message .= ' | '.acym_translation('ACYM_MESSAGE_DELETED');
1208 - $this->deleteMessage($this->_message->messageNB);
1209 - }
1210 -
1211 - return $message;
1212 - }
1213 -
1214 - private function decodeAddressImap(string $type): void
1215 - {
1216 - $address = $type.'address';
1217 - $name = $type.'_name';
1218 - $email = $type.'_email';
1219 - if (empty($this->_message->$type)) {
1220 - return;
1221 - }
1222 -
1223 - $var = $this->_message->$type;
1224 -
1225 - if (empty($this->_message->header)) {
1226 - $this->_message->header = new \stdClass();
1227 - }
1228 -
1229 - if (!empty($this->_message->$address)) {
1230 - $this->_message->header->$name = $this->_message->$address;
1231 - } else {
1232 - $this->_message->header->$name = $var[0]->personal;
1233 - }
1234 -
1235 - $this->_message->header->$email = $var[0]->mailbox.'@'.@$var[0]->host;
1236 - }
1237 -
1238 - protected function display(string $message, int $type = self::MESSAGE_TYPE_SUCCESS, string $num = ''): void
1239 - {
1240 - $this->messages[] = $message;
1241 -
1242 - if (!$this->report) return;
1243 -
1244 - if ($type === self::MESSAGE_TYPE_SUCCESS) {
1245 - $color = 'green';
1246 - } elseif ($type === self::MESSAGE_TYPE_INFO) {
1247 - $color = 'blue';
1248 - } else {
1249 - $color = 'red';
1250 - }
1251 -
1252 - if (!empty($num)) {
1253 - echo '<br />'.$num.' : ';
1254 - } else {
1255 - echo '<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
1256 - }
1257 -
1258 - echo '<span style="color: '.$color.'">'.$message.'</span>';
1259 -
1260 - if (function_exists('ob_flush')) {
1261 - @ob_flush();
1262 - }
1263 - if (!$this->mod_security2) {
1264 - @flush();
1265 - }
1266 - }
1267 -
1268 - public function decodeHeader(string $input): string
1269 - {
1270 - $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
1271 - $currentCharset = false;
1272 -
1273 - while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
1274 - $encoded = $matches[1];
1275 - $charset = $matches[2];
1276 - $encoding = $matches[3];
1277 - $text = $matches[4];
1278 -
1279 - switch (strtolower($encoding)) {
1280 - case 'b':
1281 - $text = base64_decode($text);
1282 - break;
1283 -
1284 - case 'q':
1285 - $text = str_replace('_', ' ', $text);
1286 - preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
1287 - foreach ($matches[1] as $value) {
1288 - $text = str_replace('='.$value, chr(hexdec($value)), $text);
1289 - }
1290 - break;
1291 - }
1292 - $currentCharset = $charset;
1293 - $input = str_replace($encoded, $text, $input);
1294 - }
1295 -
1296 - if (!empty($currentCharset) && in_array($currentCharset, $this->allCharsets)) {
1297 - $input = $this->encodingHelper->change($input, $currentCharset, 'UTF-8');
1298 - }
1299 -
1300 - return $input;
1301 - }
1302 -
1303 - private function explodeBodyMixed(object $struct, string $path = '1'): array
1304 - {
1305 - $allParts = [];
1306 -
1307 - if (empty($struct->parts)) {
1308 - return $allParts;
1309 - }
1310 -
1311 - $pathPrefix = ($struct->subtype === 'MIXED' && $path === '1') ? '' : $path.'.';
1312 - foreach ($struct->parts as $i => $part) {
1313 - $partPath = $pathPrefix.($i + 1);
1314 - if ($part->type == 1) {
1315 - $allParts = array_merge($this->explodeBodyMixed($part, $partPath), $allParts);
1316 - } else {
1317 - $allParts[$partPath] = $part;
1318 - }
1319 - }
1320 -
1321 - return $allParts;
1322 - }
1323 -
1324 - private function explodeBody(object $struct, string $path = '0', bool $inline = false): array
1325 - {
1326 - $allParts = [];
1327 -
1328 - if (empty($struct->parts)) {
1329 - return $allParts;
1330 - }
1331 -
1332 - $c = 0;
1333 - foreach ($struct->parts as $part) {
1334 - if ($part->type == 1) {
1335 - if ($part->subtype === 'MIXED') {
1336 - $path = $this->incPath($path);
1337 - $newpath = $path.'.0';
1338 - $allParts = array_merge($this->explodeBody($part, $newpath), $allParts);
1339 - } else {
1340 - $newpath = $this->incPath($path);
1341 - $path = $this->incPath($path);
1342 - $allParts = array_merge($this->explodeBody($part, $newpath, true), $allParts);
1343 - }
1344 - } else {
1345 - $c++;
1346 - if ($c == 1 && $inline) {
1347 - $path = $path.'.0';
1348 - }
1349 - $path = $this->incPath($path);
1350 - $allParts[$path] = $part;
1351 - }
1352 - }
1353 -
1354 - return $allParts;
1355 - }
1356 -
1357 - private function incPath(string $path): string
1358 - {
1359 - $newPath = '';
1360 - $path_elements = explode('.', $path);
1361 - $limit = count($path_elements);
1362 - for ($i = 0; $i < $limit; $i++) {
1363 - if ($i == $limit - 1) {
1364 - $newPath .= $path_elements[$i] + 1;
1365 - } else {
1366 - $newPath .= $path_elements[$i].'.';
1367 - }
1368 - }
1369 -
1370 - return $newPath;
1371 - }
1372 -
1373 - private function decodeContent(string $content, object $structure): string
1374 - {
1375 - $encoding = $structure->encoding;
1376 -
1377 - if ($encoding == 2) {
1378 - $content = imap_binary($content);
1379 - } elseif ($encoding == 3) {
1380 - $content = base64_decode($content, true);
1381 - } elseif ($encoding == 4) {
1382 - $content = imap_qprint($content);
1383 - }
1384 -
1385 - if (!empty($this->action)) {
1386 - $charset = $this->getMailParam($structure);
1387 - if (!empty($charset) && strtoupper($charset) !== 'UTF-8') {
1388 - $content = $this->encodingHelper->change($content, $charset, 'UTF-8');
1389 - }
1390 -
1391 - return $content;
1392 - }
1393 -
1394 - return substr($content, 0, 100000);
1395 - }
1396 -
1397 - private function getMailParam(object $params): string
1398 - {
1399 - $searchIn = [];
1400 -
1401 - if ($params->ifparameters) {
1402 - $searchIn = array_merge($searchIn, $params->parameters);
1403 - }
1404 - if ($params->ifdparameters) {
1405 - $searchIn = array_merge($searchIn, $params->dparameters);
1406 - }
1407 -
1408 - if (empty($searchIn)) {
1409 - return '';
1410 - }
1411 -
1412 - foreach ($searchIn as $values) {
1413 - if (strtolower($values->attribute) === 'charset') {
1414 - return $values->value;
1415 - }
1416 - }
1417 -
1418 - return '';
1419 - }
1420 -
1421 - public function getErrors(): array
1422 - {
1423 - $return = [];
1424 - if (!$this->usePear) {
1425 - if (!function_exists('imap_alerts')) {
1426 - $return[] = 'The IMAP extension could not be loaded, please change your PHP configuration to enable it or use the pop3 method without imap extension';
1427 -
1428 - return $return;
1429 - }
1430 - $alerts = $this->callImapFunction('imap_alerts');
1431 - $errors = $this->callImapFunction('imap_errors');
1432 - if (!empty($alerts)) {
1433 - $return = array_merge($return, $alerts);
1434 - }
1435 - if (!empty($errors)) {
1436 - $return = array_merge($return, $errors);
1437 - }
1438 - }
1439 -
1440 - return $return;
1441 - }
1442 -
1443 - private function refreshToken(): void
1444 - {
1445 - if ($this->config->get('bounce_server') === 'imap.gmail.com') {
1446 - $url = 'https://oauth2.googleapis.com/token';
1447 - } else {
1448 - $tenant = $this->config->get('bounce_tenant', 'consumers');
1449 - $url = 'https://login.microsoftonline.com/'.$tenant.'/oauth2/v2.0/token';
1450 - }
1451 -
1452 - $oauth = new OAuth(
1453 - [
1454 - 'sendingMethod' => 'bounce',
1455 - 'tokenGenerationUrl' => $url,
1456 - 'userName' => trim($this->config->get('bounce_username')),
1457 - 'clientId' => trim($this->config->get('bounce_client_id')),
1458 - 'clientSecret' => trim($this->config->get('bounce_client_secret')),
1459 - 'oauthToken' => trim($this->config->get('bounce_access_token')),
1460 - 'oauthTokenExpiration' => trim($this->config->get('bounce_access_token_expiration')),
1461 - 'refreshToken' => trim($this->config->get('bounce_refresh_token')),
1462 - ]
1463 - );
1464 -
1465 - $this->oAuthToken = $oauth->getToken();
1466 - }
1467 -
1468 - private function callImapFunction(string $functionName, array $params = [])
1469 - {
1470 - if (in_array($this->server, self::HOSTS_NEEDING_OAUTH)) {
1471 - $functionName = str_replace('imap_', 'imap2_', $functionName);
1472 - }
1473 -
1474 - if (empty($params)) {
1475 - return $functionName();
1476 - }
1477 -
1478 - return $functionName(...$params);
1479 - }
1480 -}