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