| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace AcyMailing\Helpers; |
| 4 | 4 | |
| 5 | +use AcyMailerPhp\OAuth; | |
| 5 | 6 | use AcyMailing\Classes\HistoryClass; |
| 6 | 7 | use AcyMailing\Classes\ListClass; |
| 7 | 8 | use AcyMailing\Classes\MailClass; |
| 8 | 9 | use AcyMailing\Classes\RuleClass; |
| @@ -12,8 +13,18 @@ | ||
| 12 | 13 | use AcyMailing\Types\CharsetType; |
| 13 | 14 | |
| 14 | 15 | class BounceHelper extends AcymObject |
| 15 | 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 | + // Needed information for the connection | |
| 16 | 27 | private string $server; |
| 17 | 28 | private string $username; |
| 18 | 29 | private string $password; |
| 19 | 30 | private string $port; |
| @@ -21,10 +32,10 @@ | ||
| 21 | 32 | private string $secureMethod; |
| 22 | 33 | private bool $selfSigned; |
| 23 | 34 | private int $timeout; |
| 24 | 35 | private string $oAuthToken; |
| 25 | - private string $imapConnectionMethod; | |
| 26 | 36 | |
| 37 | + // Allowed extensions for uploaded files (attachments) | |
| 27 | 38 | private array $allowed_extensions = []; |
| 28 | 39 | protected int $nbMessages = 0; |
| 29 | 40 | public bool $report = false; |
| 30 | 41 | private MailerHelper $mailer; |
| @@ -37,16 +48,21 @@ | ||
| 37 | 48 | private bool $usePear = false; |
| 38 | 49 | private string $detectEmail; |
| 39 | 50 | private string $detectEmail2 = '/(([a-z0-9\-]+\.)+[a-z0-9]{2,8})\/([a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*)/i'; |
| 40 | 51 | public array $messages = []; |
| 52 | + //Max execution time minus 4 seconds... we need to stop the process before this date. | |
| 41 | 53 | public int $stoptime = 0; |
| 54 | + //Check apache module "mod_security" to avoid flush issue | |
| 42 | 55 | protected bool $mod_security2 = false; |
| 56 | + //Number of ob_end_flush used in the process... | |
| 43 | 57 | public int $obend = 0; |
| 58 | + // Correspond to mailbox entity in database | |
| 44 | 59 | public object $action; |
| 45 | 60 | public array $attachments = []; |
| 61 | + | |
| 46 | 62 | private array $inlineImages = []; |
| 47 | - | |
| 48 | 63 | private array $allCharsets; |
| 64 | + private bool $errorDuringActions = false; | |
| 49 | 65 | |
| 50 | 66 | private RuleClass $ruleClass; |
| 51 | 67 | private MailClass $mailClass; |
| 52 | 68 | private HistoryClass $historyClass; |
| @@ -86,10 +102,9 @@ | ||
| 86 | 102 | 'connect_method' => $this->config->get('bounce_connection'), |
| 87 | 103 | 'secure_method' => $this->config->get('bounce_secured'), |
| 88 | 104 | 'self_signed' => $this->config->get('bounce_certif', false), |
| 89 | 105 | 'timeout' => $this->config->get('bounce_timeout', 10), |
| 90 | - 'bounce_token' => $this->config->get('bounce_token'), | |
| 91 | - 'imap_connection_method' => $this->config->get('imap_connection_method', 'classic'), | |
| 106 | + 'bounce_access_token' => $this->config->get('bounce_access_token'), | |
| 92 | 107 | ]; |
| 93 | 108 | } |
| 94 | 109 | |
| 95 | 110 | $this->server = $config['server']; |
| @@ -99,10 +114,9 @@ | ||
| 99 | 114 | $this->connectMethod = $config['connect_method']; |
| 100 | 115 | $this->secureMethod = $config['secure_method']; |
| 101 | 116 | $this->selfSigned = $config['self_signed']; |
| 102 | 117 | $this->timeout = intval($config['timeout']); |
| 103 | - $this->oAuthToken = str_replace('Bearer ', '', $config['bounce_token']); | |
| 104 | - $this->imapConnectionMethod = $config['imap_connection_method']; | |
| 118 | + $this->oAuthToken = $config['bounce_access_token']; | |
| 105 | 119 | |
| 106 | 120 | if ($this->connectMethod === 'pear') { |
| 107 | 121 | $this->usePear = true; |
| 108 | 122 | include_once ACYM_LIBRARIES.'Pear'.DS.'Pop3.php'; |
| @@ -109,36 +123,22 @@ | ||
| 109 | 123 | |
| 110 | 124 | return true; |
| 111 | 125 | } |
| 112 | 126 | |
| 127 | + require_once ACYM_LIBRARIES.'Imap2'.DS.'bootstrap.php'; | |
| 128 | + | |
| 113 | 129 | if (extension_loaded('imap') || function_exists('imap_open')) { |
| 114 | 130 | return true; |
| 115 | 131 | } |
| 116 | 132 | |
| 117 | - $prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : ''; | |
| 118 | - $EXTENSION = $prefix.'imap.'.PHP_SHLIB_SUFFIX; | |
| 133 | + if ($this->report) { | |
| 134 | + $prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : ''; | |
| 135 | + $extension = $prefix.'imap.'.PHP_SHLIB_SUFFIX; | |
| 119 | 136 | |
| 120 | - if (function_exists('dl')) { | |
| 121 | - $fatalMessage = 'The system tried to load dynamically the '.$EXTENSION.' extension'; | |
| 122 | - $fatalMessage .= '<br />If you see this message, that means the system could not load this PHP extension'; | |
| 123 | - $fatalMessage .= '<br />Please enable the PHP Extension '.$EXTENSION; | |
| 124 | - ob_start(); | |
| 125 | - echo $fatalMessage; | |
| 126 | - dl($EXTENSION); | |
| 127 | - $warnings = str_replace($fatalMessage, '', ob_get_clean()); | |
| 128 | - if (extension_loaded('imap') || function_exists('imap_open')) { | |
| 129 | - return true; | |
| 130 | - } | |
| 131 | - } | |
| 132 | - | |
| 133 | - if ($this->report) { | |
| 134 | 137 | acym_display( |
| 135 | - 'The extension "'.$EXTENSION.'" could not be loaded, please change your PHP configuration to enable it or use the pop3 method without imap extension', | |
| 138 | + 'The extension "'.$extension.'" could not be loaded, please change your PHP configuration to enable it or use the pop3 method without imap extension', | |
| 136 | 139 | 'error' |
| 137 | 140 | ); |
| 138 | - if (!empty($warnings)) { | |
| 139 | - acym_display($warnings, 'warning'); | |
| 140 | - } | |
| 141 | 141 | } |
| 142 | 142 | |
| 143 | 143 | return false; |
| 144 | 144 | } |
| @@ -165,9 +165,9 @@ | ||
| 165 | 165 | |
| 166 | 166 | $port = intval($this->port); |
| 167 | 167 | $secure = $this->secureMethod; |
| 168 | 168 | if (empty($port)) { |
| 169 | - if ($secure == 'ssl') { | |
| 169 | + if ($secure === 'ssl') { | |
| 170 | 170 | $port = '995'; |
| 171 | 171 | } else { |
| 172 | 172 | $port = '110/pop3/notls'; |
| 173 | 173 | } |
| @@ -174,8 +174,9 @@ | ||
| 174 | 174 | } |
| 175 | 175 | |
| 176 | 176 | $serverName = trim($this->server); |
| 177 | 177 | |
| 178 | + //We don't add back the ssl:// or tls:// if it's already there | |
| 178 | 179 | if (!empty($secure) && !strpos($serverName, '://')) { |
| 179 | 180 | $serverName = $secure.'://'.$serverName; |
| 180 | 181 | } |
| 181 | 182 | |
| @@ -181,9 +182,13 @@ | ||
| 181 | 182 | |
| 182 | 183 | if (!$this->mailbox->connect($serverName, $port)) { |
| 183 | 184 | $warnings = ob_get_clean(); |
| 184 | 185 | if ($this->report) { |
| 185 | - acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_CONNECTING', $this->server.' : '.$port), 'error'); | |
| 186 | + $notification = [ | |
| 187 | + 'name' => 'error_connecting', | |
| 188 | + 'removable' => 1, | |
| 189 | + ]; | |
| 190 | + acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_CONNECTING', $this->server.' : '.$port), 'error', true, [$notification]); | |
| 186 | 191 | } |
| 187 | 192 | if (!empty($warnings) && $this->report) { |
| 188 | 193 | acym_display($warnings, 'warning'); |
| 189 | 194 | } |
| @@ -194,9 +199,9 @@ | ||
| 194 | 199 | $login = $this->mailbox->login(trim($this->username), trim($this->password), 'USER'); |
| 195 | 200 | if (empty($login) || isset($login->code)) { |
| 196 | 201 | $warnings = ob_get_clean(); |
| 197 | 202 | if ($this->report) { |
| 198 | - acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_LOGIN', $this->username.':'.$this->password), 'error'); | |
| 203 | + acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_LOGIN', $this->username), 'error'); | |
| 199 | 204 | } |
| 200 | 205 | if (!empty($warnings) && $this->report) { |
| 201 | 206 | acym_display($warnings, 'warning'); |
| 202 | 207 | } |
| @@ -217,8 +222,9 @@ | ||
| 217 | 222 | return false; |
| 218 | 223 | } |
| 219 | 224 | |
| 220 | 225 | ob_start(); |
| 226 | + //First we reset the buffer or errors and warnings | |
| 221 | 227 | $buff = $this->callImapFunction('imap_alerts', []); |
| 222 | 228 | $buff = $this->callImapFunction('imap_errors', []); |
| 223 | 229 | |
| 224 | 230 | $timeout = intval($this->timeout); |
| @@ -241,16 +247,18 @@ | ||
| 241 | 247 | if (!empty($port)) { |
| 242 | 248 | $serverName .= ':'.$port; |
| 243 | 249 | } |
| 244 | 250 | |
| 245 | - if ($serverName === '{outlook.office365.com:993') { | |
| 251 | + if ($this->server === 'outlook.office365.com') { | |
| 246 | 252 | $serverName .= '/imap'; |
| 247 | 253 | } |
| 248 | 254 | |
| 255 | + //Add the secure protocol (TLS or SSL) | |
| 249 | 256 | if (!empty($secure)) { |
| 250 | 257 | $serverName .= '/'.$secure; |
| 251 | 258 | } |
| 252 | 259 | |
| 260 | + // Test if string contains '\' | |
| 253 | 261 | $email = trim($this->username); |
| 254 | 262 | if (strpos($email, '\\') !== false) { |
| 255 | 263 | [$user, $authuser] = explode('\\', $email); |
| 256 | 264 | [$x, $domain] = explode('@', $user); |
| @@ -260,14 +268,18 @@ | ||
| 260 | 268 | if ($this->selfSigned) { |
| 261 | 269 | $serverName .= '/novalidate-cert'; |
| 262 | 270 | } |
| 263 | 271 | |
| 264 | - if (!empty($protocol)) { | |
| 272 | + //Add the method (imap by default) ex : pop3 | |
| 273 | + if (!empty($protocol) && $this->server !== 'outlook.office365.com') { | |
| 265 | 274 | $serverName .= '/service='.$protocol; |
| 266 | 275 | } |
| 267 | 276 | $serverName .= '}'; |
| 268 | - if ($this->imapConnectionMethod === 'oauth') { | |
| 277 | + if (in_array($this->server, self::HOSTS_NEEDING_OAUTH)) { | |
| 269 | 278 | $this->refreshToken(); |
| 279 | + if (empty($this->oAuthToken)) { | |
| 280 | + return false; | |
| 281 | + } | |
| 270 | 282 | $this->mailbox = imap2_open($serverName, trim($this->username), $this->oAuthToken, OP_XOAUTH2); |
| 271 | 283 | } else { |
| 272 | 284 | $this->mailbox = imap_open($serverName, trim($this->username), trim($this->password), OP_SILENT); |
| 273 | 285 | } |
| @@ -274,9 +286,13 @@ | ||
| 274 | 286 | $warnings = ob_get_clean(); |
| 275 | 287 | |
| 276 | 288 | if ($this->report) { |
| 277 | 289 | if (!$this->mailbox) { |
| 278 | - acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_CONNECTING', $serverName), 'error'); | |
| 290 | + $notification = [ | |
| 291 | + 'name' => 'error_connecting', | |
| 292 | + 'removable' => 1, | |
| 293 | + ]; | |
| 294 | + acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_CONNECTING', $serverName), 'error', true, [$notification]); | |
| 279 | 295 | } |
| 280 | 296 | if (!empty($warnings)) { |
| 281 | 297 | acym_enqueueMessage($warnings, 'warning'); |
| 282 | 298 | } |
| @@ -342,9 +358,12 @@ | ||
| 342 | 358 | private function decodeMessagePear(bool $attachments): bool |
| 343 | 359 | { |
| 344 | 360 | $this->_message->headerinfo = $this->mailbox->getParsedHeaders($this->_message->messageNB); |
| 345 | 361 | if (empty($this->_message->headerinfo['subject'])) { |
| 346 | - return false; | |
| 362 | + if (empty($this->_message->headerinfo['Subject'])) { | |
| 363 | + return false; | |
| 364 | + } | |
| 365 | + $this->_message->headerinfo['subject'] = $this->_message->headerinfo['Subject']; | |
| 347 | 366 | } |
| 348 | 367 | $this->_message->text = ''; |
| 349 | 368 | $this->_message->html = $this->mailbox->getBody($this->_message->messageNB); |
| 350 | 369 | |
| @@ -356,9 +375,11 @@ | ||
| 356 | 375 | $inlineImages = []; |
| 357 | 376 | |
| 358 | 377 | $segments = explode('--'.$matches[1], $this->_message->html); |
| 359 | 378 | foreach ($segments as $segment) { |
| 379 | + // Find the segment containing the html and text version | |
| 360 | 380 | if (strpos($segment, 'Content-Type: text/plain') !== false) { |
| 381 | + // Check if there is another boundary | |
| 361 | 382 | $matches = []; |
| 362 | 383 | preg_match('#boundary="([^"]+)"#i', $segment, $matches); |
| 363 | 384 | |
| 364 | 385 | if (!empty($matches[1])) { |
| @@ -369,8 +390,9 @@ | ||
| 369 | 390 | |
| 370 | 391 | if (strpos($onePart, 'Content-Transfer-Encoding') !== false) { |
| 371 | 392 | preg_match('#Content-Transfer-Encoding: (.+)#i', $onePart, $encoding); |
| 372 | 393 | $encoding = trim($encoding[1]); |
| 394 | + // Someone has an issue when the $encoding is equal to 'Content-Type:' only | |
| 373 | 395 | if (!empty($encoding) && $encoding !== 'Content-Type:') { |
| 374 | 396 | $content = mb_convert_encoding($content, 'UTF-8', $encoding); |
| 375 | 397 | } |
| 376 | 398 | } |
| @@ -384,9 +406,9 @@ | ||
| 384 | 406 | } |
| 385 | 407 | } else { |
| 386 | 408 | $this->_message->html .= ' '.trim(preg_replace('#(charset=".*?\r\n)|Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n#is', "", $segment)); |
| 387 | 409 | } |
| 388 | - } elseif (preg_match("#Content-Type: .*?/(png|jpg|jpeg|gif)#i", $segment) !== false) { | |
| 410 | + } elseif (preg_match("#Content-Type: .*?/(png|jpg|jpeg|gif)#i", $segment) === 1) { | |
| 389 | 411 | preg_match('#name="([^"]+)"#i', $segment, $filename); |
| 390 | 412 | if (empty($filename) || empty($filename[1])) { |
| 391 | 413 | continue; |
| 392 | 414 | } |
| @@ -399,13 +421,20 @@ | ||
| 399 | 421 | } |
| 400 | 422 | |
| 401 | 423 | $extension = substr($filename, $extensionPos + 1); |
| 402 | 424 | |
| 425 | + if (!in_array(strtolower($extension), $this->allowed_extensions) || acym_isForbiddenFileExtension($filename)) { | |
| 426 | + continue; | |
| 427 | + } | |
| 428 | + | |
| 429 | + // Make sure there is no double extension or space | |
| 403 | 430 | $filename = preg_replace('#[^a-zA-Z0-9]#Uis', '_', substr($filename, 0, $extensionPos)); |
| 404 | 431 | |
| 432 | + // Get the upload folder and create it if needed | |
| 405 | 433 | $uploadFolder = str_replace(['/', '\\'], DS, acym_getFilesFolder()); |
| 406 | 434 | $pathToUpload = ACYM_ROOT.trim($uploadFolder, DS).DS; |
| 407 | 435 | |
| 436 | + // Rename if needed | |
| 408 | 437 | if (file_exists($pathToUpload.$filename.'.'.$extension)) { |
| 409 | 438 | $fileNumber = 1; |
| 410 | 439 | while (file_exists($pathToUpload.$filename.'_('.$fileNumber.').'.$extension)) { |
| 411 | 440 | $fileNumber++; |
| @@ -412,9 +441,9 @@ | ||
| 412 | 441 | } |
| 413 | 442 | $filename = $filename.'_('.$fileNumber.')'; |
| 414 | 443 | } |
| 415 | 444 | |
| 416 | - if ($attachments && preg_match("#Content-Disposition: attachment;#i", $segment) !== false) { | |
| 445 | + if ($attachments && preg_match("#Content-Disposition: attachment;#i", $segment) === 1) { | |
| 417 | 446 | $data = trim(substr($segment, strpos($segment, "\r\n\r\n"))); |
| 418 | 447 | if (strpos($segment, 'Content-Transfer-Encoding: base64') !== false) { |
| 419 | 448 | $data = base64_decode($data); |
| 420 | 449 | } |
| @@ -427,9 +456,9 @@ | ||
| 427 | 456 | $this->attachments[] = $attachment; |
| 428 | 457 | } |
| 429 | 458 | } catch (\Exception $e) { |
| 430 | 459 | $completeFilename = $filename.'.'.$extension; |
| 431 | - $this->display(acym_translationSprintf('ACYM_ERROR_UPLOADING_ATTACHMENT_X_TO_X', $completeFilename, $e->getMessage()), false); | |
| 460 | + $this->display(acym_translationSprintf('ACYM_ERROR_UPLOADING_ATTACHMENT_X_TO_X', $completeFilename, $e->getMessage()), self::MESSAGE_TYPE_ERROR); | |
| 432 | 461 | } |
| 433 | 462 | } else { |
| 434 | 463 | preg_match('#Content-ID: <([^>]+)>#i', $segment, $contentID); |
| 435 | 464 | if (empty($contentID) || empty($contentID[1])) { |
| @@ -447,9 +476,9 @@ | ||
| 447 | 476 | if (acym_writeFile($pathToUpload.$filename.'.'.$extension, $data)) { |
| 448 | 477 | $inlineImages['cid:'.$contentID] = acym_rootURI().$uploadFolder.'/'.$filename.'.'.$extension; |
| 449 | 478 | } |
| 450 | 479 | } catch (\Exception $e) { |
| 451 | - $this->display(acym_translationSprintf('ACYM_ERROR_UPLOAD_ATTACHMENT', $filename.'.'.$extension, $e->getMessage()), false); | |
| 480 | + $this->display(acym_translationSprintf('ACYM_ERROR_UPLOAD_ATTACHMENT', $filename.'.'.$extension, $e->getMessage()), self::MESSAGE_TYPE_ERROR); | |
| 452 | 481 | } |
| 453 | 482 | } |
| 454 | 483 | } |
| 455 | 484 | } |
| @@ -467,12 +496,12 @@ | ||
| 467 | 496 | $this->_message->header->sender_email = @$this->_message->headerinfo['return-path']; |
| 468 | 497 | if (is_array($this->_message->header->sender_email)) { |
| 469 | 498 | $this->_message->header->sender_email = reset($this->_message->header->sender_email); |
| 470 | 499 | } |
| 471 | - if (preg_match($this->detectEmail, $this->_message->header->sender_email, $results)) { | |
| 500 | + if (!empty($this->_message->header->sender_email) && preg_match($this->detectEmail, $this->_message->header->sender_email, $results)) { | |
| 472 | 501 | $this->_message->header->sender_email = $results[0]; |
| 473 | 502 | } |
| 474 | - $this->_message->header->sender_name = strip_tags(@$this->_message->headerinfo['from']); | |
| 503 | + $this->_message->header->sender_name = acym_stripTags(@$this->_message->headerinfo['from'] ?? ''); | |
| 475 | 504 | $this->_message->header->reply_to_email = $this->_message->header->sender_email; |
| 476 | 505 | $this->_message->header->reply_to_name = $this->_message->header->sender_name; |
| 477 | 506 | $this->_message->header->from_email = $this->_message->header->sender_email; |
| 478 | 507 | $this->_message->header->from_name = $this->_message->header->sender_name; |
| @@ -481,9 +510,8 @@ | ||
| 481 | 510 | } |
| 482 | 511 | |
| 483 | 512 | private function decodeMessageImap(bool $attachments): bool |
| 484 | 513 | { |
| 485 | - | |
| 486 | 514 | $this->_message->structure = $this->callImapFunction('imap_fetchstructure', [$this->mailbox, $this->_message->messageNB]); |
| 487 | 515 | |
| 488 | 516 | if (empty($this->_message->structure)) { |
| 489 | 517 | return false; |
| @@ -493,11 +521,12 @@ | ||
| 493 | 521 | |
| 494 | 522 | $this->_message->html = ''; |
| 495 | 523 | $this->_message->text = ''; |
| 496 | 524 | |
| 525 | + //Multipart message : type == 1 | |
| 497 | 526 | if ($this->_message->structure->type == 1) { |
| 498 | 527 | $this->_message->contentType = 2; |
| 499 | - if ($this->_message->structure->subtype == "MIXED") { | |
| 528 | + if ($this->_message->structure->subtype === 'MIXED') { | |
| 500 | 529 | $allParts = $this->explodeBodyMixed($this->_message->structure); |
| 501 | 530 | } else { |
| 502 | 531 | $allParts = $this->explodeBody($this->_message->structure); |
| 503 | 532 | } |
| @@ -503,18 +532,20 @@ | ||
| 503 | 532 | } |
| 504 | 533 | |
| 505 | 534 | $text = ''; |
| 506 | 535 | foreach ($allParts as $num => $onePart) { |
| 507 | - $decodedContent = $this->decodeContent($this->callImapFunction('imap_fetchbody', [$this->mailbox, $this->_message->messageNB, $num]), $onePart); | |
| 508 | - if ($onePart->subtype == 'HTML') { | |
| 536 | + $content = $this->callImapFunction('imap_fetchbody', [$this->mailbox, $this->_message->messageNB, $num]); | |
| 537 | + $decodedContent = empty($content) ? '' : $this->decodeContent($content, $onePart); | |
| 538 | + if ($onePart->subtype === 'HTML') { | |
| 509 | 539 | $this->_message->html .= $decodedContent; |
| 510 | 540 | } else { |
| 511 | - if ($onePart->subtype == 'PLAIN') { | |
| 541 | + if ($onePart->subtype === 'PLAIN') { | |
| 512 | 542 | $text .= $decodedContent."\n"; |
| 513 | 543 | } |
| 514 | 544 | $this->_message->text .= $decodedContent."\n\n- - -\n\n"; |
| 515 | 545 | } |
| 516 | 546 | |
| 547 | + // Only for mailbox actions | |
| 517 | 548 | if (!empty($this->action) && $attachments) { |
| 518 | 549 | if ((isset($onePart->parameters) && is_array($onePart->parameters)) || (isset($onePart->dparameters) && is_array($onePart->dparameters))) { |
| 519 | 550 | $this->uploadAttachment($onePart, $num); |
| 520 | 551 | } elseif (isset($onePart->parts) && is_array($onePart->parts)) { |
| @@ -531,18 +562,25 @@ | ||
| 531 | 562 | if (!empty($this->inlineImages)) { |
| 532 | 563 | $this->_message->html = str_replace(array_keys($this->inlineImages), $this->inlineImages, $this->_message->html); |
| 533 | 564 | } |
| 534 | 565 | } else { |
| 535 | - if ($this->_message->structure->subtype == 'HTML') { | |
| 566 | + $body = $this->callImapFunction('imap_body', [$this->mailbox, $this->_message->messageNB]); | |
| 567 | + $decodedBody = empty($body) ? '' : $this->decodeContent($body, $this->_message->structure); | |
| 568 | + if ($this->_message->structure->subtype === 'HTML') { | |
| 536 | 569 | $this->_message->contentType = 1; |
| 537 | - $this->_message->html = $this->decodeContent($this->callImapFunction('imap_body', [$this->mailbox, $this->_message->messageNB]), $this->_message->structure); | |
| 570 | + $this->_message->html = $decodedBody; | |
| 538 | 571 | } else { |
| 539 | 572 | $this->_message->contentType = 0; |
| 540 | - $this->_message->text = $this->decodeContent($this->callImapFunction('imap_body', [$this->mailbox, $this->_message->messageNB]), $this->_message->structure); | |
| 573 | + $this->_message->text = $decodedBody; | |
| 541 | 574 | } |
| 542 | 575 | } |
| 543 | 576 | |
| 544 | - $this->_message->subject = $this->decodeHeader($this->_message->subject); | |
| 577 | + //Decode the subject | |
| 578 | + if (!empty($this->_message->subject)) { | |
| 579 | + $this->_message->subject = $this->decodeHeader($this->_message->subject); | |
| 580 | + } else { | |
| 581 | + $this->_message->subject = ''; | |
| 582 | + } | |
| 545 | 583 | |
| 546 | 584 | $this->decodeAddressImap('sender'); |
| 547 | 585 | $this->decodeAddressImap('from'); |
| 548 | 586 | $this->decodeAddressImap('reply_to'); |
| @@ -552,8 +590,9 @@ | ||
| 552 | 590 | } |
| 553 | 591 | |
| 554 | 592 | private function uploadAttachment($attachment, $num): void |
| 555 | 593 | { |
| 594 | + // It may be a real attachment, or an inline image | |
| 556 | 595 | if (!isset($attachment->disposition) || (strtolower($attachment->disposition) !== 'attachment' && strtolower($attachment->disposition) !== 'inline')) { |
| 557 | 596 | return; |
| 558 | 597 | } |
| 559 | 598 | |
| @@ -562,9 +601,9 @@ | ||
| 562 | 601 | $extensionPos = strrpos($filename, '.'); |
| 563 | 602 | |
| 564 | 603 | if ($extensionPos === false) { |
| 565 | 604 | foreach ($attachment->parameters as $key => $oneParam) { |
| 566 | - if (strtolower($oneParam->attribute) == 'name') { | |
| 605 | + if (strtolower($oneParam->attribute) === 'name') { | |
| 567 | 606 | $filename = $this->decodeHeader($attachment->parameters[$key]->value); |
| 568 | 607 | $extensionPos = strrpos($filename, '.'); |
| 569 | 608 | break; |
| 570 | 609 | } |
| @@ -570,22 +609,27 @@ | ||
| 570 | 609 | } |
| 571 | 610 | } |
| 572 | 611 | } |
| 573 | 612 | |
| 613 | + // No extension => no upload | |
| 574 | 614 | if ($extensionPos === false) { |
| 575 | 615 | return; |
| 576 | 616 | } |
| 577 | 617 | |
| 618 | + // Upload only allowed extensions in the configuration, and never executable ones | |
| 578 | 619 | $extension = substr($filename, $extensionPos + 1); |
| 579 | - if (!in_array(strtolower($extension), $this->allowed_extensions)) { | |
| 620 | + if (!in_array(strtolower($extension), $this->allowed_extensions) || acym_isForbiddenFileExtension($filename)) { | |
| 580 | 621 | return; |
| 581 | 622 | } |
| 582 | 623 | |
| 624 | + // Make sure there is no double extension or space | |
| 583 | 625 | $filename = preg_replace('#[^a-zA-Z0-9]#Uis', '_', substr($filename, 0, $extensionPos)); |
| 584 | 626 | |
| 627 | + // Get the upload folder and create it if needed | |
| 585 | 628 | $uploadFolder = str_replace(['/', '\\'], DS, acym_getFilesFolder()); |
| 586 | 629 | $pathToUpload = ACYM_ROOT.trim($uploadFolder, DS).DS; |
| 587 | 630 | |
| 631 | + // Rename if needed | |
| 588 | 632 | if (file_exists($pathToUpload.$filename.'.'.$extension)) { |
| 589 | 633 | $fileNumber = 1; |
| 590 | 634 | while (file_exists($pathToUpload.$filename.'_('.$fileNumber.').'.$extension)) { |
| 591 | 635 | $fileNumber++; |
| @@ -592,16 +636,24 @@ | ||
| 592 | 636 | } |
| 593 | 637 | $filename = $filename.'_('.$fileNumber.')'; |
| 594 | 638 | } |
| 595 | 639 | |
| 596 | - $data = $this->decodeContent($this->callImapFunction('imap_fetchbody', [$this->mailbox, $this->_message->messageNB, $num]), $attachment); | |
| 640 | + $content = $this->callImapFunction('imap_fetchbody', [$this->mailbox, $this->_message->messageNB, $num]); | |
| 597 | 641 | |
| 642 | + // We could not fetch the part, there is nothing to upload | |
| 643 | + if (empty($content)) { | |
| 644 | + return; | |
| 645 | + } | |
| 646 | + | |
| 647 | + $data = $this->decodeContent($content, $attachment); | |
| 648 | + | |
| 598 | 649 | try { |
| 599 | 650 | if (acym_writeFile($pathToUpload.$filename.'.'.$extension, $data)) { |
| 600 | - if (strtolower($attachment->disposition) !== 'attachment' && !empty($attachment->id) && in_array( | |
| 601 | - strtolower($attachment->subtype), | |
| 602 | - acym_getImageFileExtensions() | |
| 603 | - )) { | |
| 651 | + if ( | |
| 652 | + strtolower($attachment->disposition) !== 'attachment' | |
| 653 | + && !empty($attachment->id) | |
| 654 | + && in_array(strtolower($attachment->subtype), acym_getImageFileExtensions()) | |
| 655 | + ) { | |
| 604 | 656 | $this->inlineImages['cid:'.trim($attachment->id, '<>')] = acym_rootURI().$uploadFolder.'/'.$filename.'.'.$extension; |
| 605 | 657 | } else { |
| 606 | 658 | $attachment = new \stdClass(); |
| 607 | 659 | $attachment->filename = str_replace(DS, '/', trim($uploadFolder, '\\/')).'/'.$filename.'.'.$extension; |
| @@ -610,9 +662,9 @@ | ||
| 610 | 662 | } |
| 611 | 663 | } |
| 612 | 664 | } catch (\Exception $e) { |
| 613 | 665 | $completeFilename = $filename.'.'.$extension; |
| 614 | - $this->display(acym_translationSprintf('ACYM_ERROR_UPLOADING_ATTACHMENT_X_TO_X', $completeFilename, $e->getMessage()), false); | |
| 666 | + $this->display(acym_translationSprintf('ACYM_ERROR_UPLOADING_ATTACHMENT_X_TO_X', $completeFilename, $e->getMessage()), self::MESSAGE_TYPE_ERROR); | |
| 615 | 667 | } |
| 616 | 668 | } |
| 617 | 669 | |
| 618 | 670 | public function handleMessages(): void |
| @@ -617,8 +669,9 @@ | ||
| 617 | 669 | |
| 618 | 670 | public function handleMessages(): void |
| 619 | 671 | { |
| 620 | 672 | $maxMessages = min($this->nbMessages, $this->config->get('bounce_max', 0)); |
| 673 | + //500 messages maximum at once | |
| 621 | 674 | if (empty($maxMessages)) { |
| 622 | 675 | $maxMessages = min($this->nbMessages, 500); |
| 623 | 676 | } |
| 624 | 677 | |
| @@ -627,9 +680,12 @@ | ||
| 627 | 680 | $modules = apache_get_modules(); |
| 628 | 681 | $this->mod_security2 = in_array('mod_security2', $modules); |
| 629 | 682 | } |
| 630 | 683 | |
| 684 | + /*This is to avoid the blank page... and it apparently works! ;) */ | |
| 685 | + // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Disabled to prevent blank output pages. | |
| 631 | 686 | @ini_set('output_buffering', 'off'); |
| 687 | + // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Disabled to prevent blank output pages. | |
| 632 | 688 | @ini_set('zlib.output_compression', 0); |
| 633 | 689 | |
| 634 | 690 | |
| 635 | 691 | if (!headers_sent()) { |
| @@ -637,17 +693,19 @@ | ||
| 637 | 693 | @ob_end_flush(); |
| 638 | 694 | } |
| 639 | 695 | } |
| 640 | 696 | |
| 641 | - $disp = "<div style='position:fixed; top:3px;left:3px;background-color : white;border : 1px solid grey; padding : 3px;font-size:14px'>"; | |
| 642 | - $disp .= acym_translation('ACYM_BOUNCE_HANDLING'); | |
| 643 | - $disp .= ': <span id="counter">0</span> / '.$maxMessages; | |
| 644 | - $disp .= '</div>'; | |
| 645 | - $disp .= '<script type="text/javascript" language="javascript">'; | |
| 646 | - $disp .= 'var mycounter = document.getElementById("counter");'; | |
| 647 | - $disp .= 'function setCounter(val){ mycounter.innerHTML=val;}'; | |
| 648 | - $disp .= '</script>'; | |
| 649 | - echo $disp; | |
| 697 | + ?> | |
| 698 | + <div style="position:fixed; top:3px;left:3px;background-color : white;border : 1px solid grey; padding : 3px;font-size:14px"> | |
| 699 | + <?php echo esc_html(acym_translation('ACYM_BOUNCE_HANDLING')); ?> | |
| 700 | + : <span id="counter">0</span> / <?php echo intval($maxMessages); ?> | |
| 701 | + </div> | |
| 702 | + <script type="text/javascript" language="javascript"> | |
| 703 | + function setCounter(val) { | |
| 704 | + document.getElementById('counter').innerHTML = val; | |
| 705 | + } | |
| 706 | + </script> | |
| 707 | + <?php | |
| 650 | 708 | if (function_exists('ob_flush')) { |
| 651 | 709 | @ob_flush(); |
| 652 | 710 | } |
| 653 | 711 | if (!$this->mod_security2) { |
| @@ -654,8 +712,9 @@ | ||
| 654 | 712 | @flush(); |
| 655 | 713 | } |
| 656 | 714 | } |
| 657 | 715 | |
| 716 | + //We load all the published rules | |
| 658 | 717 | $rules = $this->ruleClass->getAll(); |
| 659 | 718 | $rules = array_filter($rules, function ($rule) { |
| 660 | 719 | return $rule->active; |
| 661 | 720 | }); |
| @@ -663,8 +722,9 @@ | ||
| 663 | 722 | $msgNB = $maxMessages; |
| 664 | 723 | $listClass = new ListClass(); |
| 665 | 724 | $this->allLists = $listClass->getAll('id'); |
| 666 | 725 | |
| 726 | + //Exclude some email addresses... | |
| 667 | 727 | $replyemail = $this->config->get('reply_email'); |
| 668 | 728 | $fromemail = $this->config->get('from_email'); |
| 669 | 729 | $bouncemail = $this->config->get('bounce_email'); |
| 670 | 730 | $removeEmails = '#('.str_replace(['%'], ['@'], $this->config->get('bounce_username')); |
| @@ -680,9 +740,9 @@ | ||
| 680 | 740 | $removeEmails .= ')#i'; |
| 681 | 741 | |
| 682 | 742 | while (($msgNB > 0) && ($this->_message = $this->getMessage($msgNB))) { |
| 683 | 743 | if ($this->report) { |
| 684 | - echo '<script type="text/javascript" language="javascript">setCounter('.($maxMessages - $msgNB + 1).')</script>'; | |
| 744 | + echo '<script type="text/javascript" language="javascript">setCounter('.esc_html($maxMessages - $msgNB + 1).')</script>'; | |
| 685 | 745 | if (function_exists('ob_flush')) { |
| 686 | 746 | @ob_flush(); |
| 687 | 747 | } |
| 688 | 748 | if (!$this->mod_security2) { |
| @@ -691,10 +751,11 @@ | ||
| 691 | 751 | } |
| 692 | 752 | $this->_message->messageNB = $msgNB; |
| 693 | 753 | $msgNB--; |
| 694 | 754 | |
| 755 | + //We could not retrieve the message... we continue with the next message | |
| 695 | 756 | if (!$this->decodeMessage()) { |
| 696 | - $this->display(acym_translation('ACYM_ERROR_RETRIEVING_MESSAGE'), false, $maxMessages - $this->_message->messageNB + 1); | |
| 757 | + $this->display(acym_translation('ACYM_ERROR_RETRIEVING_MESSAGE'), self::MESSAGE_TYPE_ERROR, $maxMessages - $this->_message->messageNB + 1); | |
| 697 | 758 | continue; |
| 698 | 759 | } |
| 699 | 760 | |
| 700 | 761 | if (empty($this->_message->subject)) { |
| @@ -701,13 +762,19 @@ | ||
| 701 | 762 | $this->_message->subject = ''; |
| 702 | 763 | } |
| 703 | 764 | |
| 704 | 765 | $this->_message->analyseText = $this->_message->html.' '.$this->_message->text; |
| 766 | + //We add the from in the list of possible e-mail address to check | |
| 705 | 767 | if (!empty($this->_message->header->from_email)) { |
| 706 | 768 | $this->_message->analyseText .= ' '.$this->_message->header->from_email; |
| 707 | 769 | } |
| 708 | - $this->display('<b>'.acym_translation('ACYM_EMAIL_SUBJECT').' : '.strip_tags($this->_message->subject).'</b>', false, $maxMessages - $this->_message->messageNB + 1); | |
| 770 | + $this->display( | |
| 771 | + '<b>'.acym_translation('ACYM_EMAIL_SUBJECT').' : '.acym_stripTags($this->_message->subject).'</b>', | |
| 772 | + self::MESSAGE_TYPE_SUCCESS, | |
| 773 | + $maxMessages - $this->_message->messageNB + 1 | |
| 774 | + ); | |
| 709 | 775 | |
| 776 | + //Identify the user and the e-mail... | |
| 710 | 777 | preg_match('#AC([0-9]+)Y([0-9]+)BA#i', $this->_message->analyseText, $resultsVars); |
| 711 | 778 | if (!empty($resultsVars[1])) { |
| 712 | 779 | $this->_message->userid = $resultsVars[1]; |
| 713 | 780 | } |
| @@ -715,13 +782,15 @@ | ||
| 715 | 782 | $this->_message->mailid = $resultsVars[2]; |
| 716 | 783 | } |
| 717 | 784 | |
| 718 | 785 | if (empty($this->_message->userid)) { |
| 786 | + //We will need the e-mail itself in that case... :p | |
| 719 | 787 | preg_match_all($this->detectEmail, $this->_message->analyseText, $results); |
| 720 | 788 | |
| 789 | + //Still no result? We try to find others | |
| 721 | 790 | if (empty($results[0])) { |
| 722 | 791 | preg_match_all($this->detectEmail2, $this->_message->analyseText, $results2); |
| 723 | - for ($i = 0 ; $i < count($results2[0]) ; $i++) { | |
| 792 | + for ($i = 0; $i < count($results2[0]); $i++) { | |
| 724 | 793 | $results[0][] = $results2[3][$i].'@'.$results2[1][$i]; |
| 725 | 794 | } |
| 726 | 795 | } |
| 727 | 796 | |
| @@ -727,10 +796,13 @@ | ||
| 727 | 796 | |
| 728 | 797 | if (!empty($results[0])) { |
| 729 | 798 | $alreadyChecked = []; |
| 730 | 799 | foreach ($results[0] as $oneEmail) { |
| 800 | + //We will find the e-mail if it's not in the list of incorrect e-mail addresses | |
| 731 | 801 | if (!preg_match($removeEmails, $oneEmail)) { |
| 802 | + //We will keep this one, so we make sure it's strtolower | |
| 732 | 803 | $this->_message->subemail = strtolower($oneEmail); |
| 804 | + //We already checked this e-mail address... no need to try it a second time | |
| 733 | 805 | if (!empty($alreadyChecked[$this->_message->subemail])) { |
| 734 | 806 | continue; |
| 735 | 807 | } |
| 736 | 808 | |
| @@ -752,15 +824,16 @@ | ||
| 752 | 824 | } |
| 753 | 825 | } |
| 754 | 826 | |
| 755 | 827 | if (empty($this->_message->mailid) && !empty($this->_message->userid)) { |
| 828 | + //We can check if we have a user and only one e-mail sent for this user, it's obviously the e-mail we just sent!! | |
| 756 | 829 | $this->_message->mailid = acym_loadResult( |
| 757 | 830 | 'SELECT `mail_id` FROM #__acym_user_stat WHERE `user_id` = '.intval($this->_message->userid).' ORDER BY `send_date` DESC' |
| 758 | 831 | ); |
| 759 | 832 | } |
| 760 | 833 | |
| 761 | - | |
| 762 | 834 | foreach ($rules as $oneRule) { |
| 835 | + //We stop as soon as we find a good rule... | |
| 763 | 836 | if ($this->handleRule($oneRule)) { |
| 764 | 837 | break; |
| 765 | 838 | } |
| 766 | 839 | } |
| @@ -768,19 +841,22 @@ | ||
| 768 | 841 | if ($msgNB % 50 == 0) { |
| 769 | 842 | $this->userActions(); |
| 770 | 843 | } |
| 771 | 844 | |
| 845 | + //We don't have time to finish the process? Ok, we stop it now! | |
| 772 | 846 | if (!empty($this->stoptime) && time() > $this->stoptime) { |
| 773 | 847 | break; |
| 774 | 848 | } |
| 775 | 849 | } |
| 776 | 850 | |
| 777 | - | |
| 778 | 851 | $this->userActions(); |
| 779 | - | |
| 780 | 852 | $this->close(); |
| 781 | 853 | } |
| 782 | 854 | |
| 855 | + /** | |
| 856 | + * Execute actions on the subscriber... and record the statistics | |
| 857 | + * We group them to not have performance issues | |
| 858 | + */ | |
| 783 | 859 | private function userActions(): void |
| 784 | 860 | { |
| 785 | 861 | if (!empty($this->deletedUsers)) { |
| 786 | 862 | acym_arrayToInteger($this->deletedUsers); |
| @@ -789,10 +865,11 @@ | ||
| 789 | 865 | } |
| 790 | 866 | |
| 791 | 867 | if (!empty($this->blockedUsers)) { |
| 792 | 868 | acym_arrayToInteger($this->blockedUsers); |
| 869 | + $this->userClass->deactivate($this->blockedUsers); | |
| 870 | + //We delete any other e-mail from the queue as well | |
| 793 | 871 | $allUsersId = implode(',', $this->blockedUsers); |
| 794 | - acym_query('UPDATE `#__acym_user` SET `active` = 0 WHERE `id` IN ('.$allUsersId.')'); | |
| 795 | 872 | acym_query('DELETE FROM `#__acym_queue` WHERE `user_id` IN ('.$allUsersId.')'); |
| 796 | 873 | $this->blockedUsers = []; |
| 797 | 874 | } |
| 798 | 875 | |
| @@ -797,8 +874,11 @@ | ||
| 797 | 874 | } |
| 798 | 875 | |
| 799 | 876 | if (!empty($this->bounceMessages)) { |
| 800 | 877 | foreach ($this->bounceMessages as $mailid => $bouncedata) { |
| 878 | + //Do we have some bounce details to update? | |
| 879 | + //If so, we will load the current bouncedetails and update it properly. | |
| 880 | + //bouncedetails is an array of bounceRule => nbTimes used | |
| 801 | 881 | |
| 802 | 882 | $updateBounceDetails = ''; |
| 803 | 883 | if (!empty($bouncedata['bouncedetails'])) { |
| 804 | 884 | $bouncedetails = acym_loadResult('SELECT `bounce_details` FROM #__acym_mail_stat WHERE mail_id = '.intval($mailid)); |
| @@ -828,8 +908,9 @@ | ||
| 828 | 908 | ',', |
| 829 | 909 | $valueInsert |
| 830 | 910 | ).' ON DUPLICATE KEY UPDATE `bounce` = `bounce` + 1, bounce_rule=VALUES(bounce_rule)' |
| 831 | 911 | ); |
| 912 | + //We updated some profiles... let's make sure we really handle only unique bounces then and don't count it twice | |
| 832 | 913 | acym_arrayToInteger($bouncedata['userids']); |
| 833 | 914 | $realUniqueBounces = acym_loadResult( |
| 834 | 915 | 'SELECT COUNT(*) |
| 835 | 916 | FROM #__acym_user_stat |
| @@ -856,8 +937,9 @@ | ||
| 856 | 937 | if (empty($regex)) { |
| 857 | 938 | return false; |
| 858 | 939 | } |
| 859 | 940 | |
| 941 | + //Do it based on the config of the rule... | |
| 860 | 942 | |
| 861 | 943 | $analyseText = ''; |
| 862 | 944 | if (in_array('senderInfo', $oneRule->executed_on)) { |
| 863 | 945 | $analyseText .= ' '; |
| @@ -867,11 +949,13 @@ | ||
| 867 | 949 | if (isset($this->_message->header->sender_email)) { |
| 868 | 950 | $analyseText .= $this->_message->header->sender_email; |
| 869 | 951 | } |
| 870 | 952 | } |
| 953 | + | |
| 871 | 954 | if (in_array('subject', $oneRule->executed_on)) { |
| 872 | 955 | $analyseText .= ' '.$this->_message->subject; |
| 873 | 956 | } |
| 957 | + | |
| 874 | 958 | if (in_array('body', $oneRule->executed_on)) { |
| 875 | 959 | if (!empty($this->_message->html)) { |
| 876 | 960 | $analyseText .= ' '.$this->_message->html; |
| 877 | 961 | } |
| @@ -879,8 +963,9 @@ | ||
| 879 | 963 | $analyseText .= ' '.$this->_message->text; |
| 880 | 964 | } |
| 881 | 965 | } |
| 882 | 966 | |
| 967 | + //Because it's easier to handle it that way... for multilines. | |
| 883 | 968 | $analyseText = str_replace(["\n", "\r", "\t"], ' ', $analyseText); |
| 884 | 969 | |
| 885 | 970 | if (!preg_match('#'.$regex.'#ims', $analyseText)) { |
| 886 | 971 | return false; |
| @@ -886,13 +971,19 @@ | ||
| 886 | 971 | return false; |
| 887 | 972 | } |
| 888 | 973 | |
| 889 | 974 | $message = acym_translation('ACYM_BOUNCE_RULE').' ['.acym_translation('ACYM_ID').' '.$oneRule->id.'] '.acym_translation($oneRule->name).' : '; |
| 975 | + $this->errorDuringActions = false; | |
| 890 | 976 | $message .= $this->actionUser($oneRule); |
| 891 | 977 | $message .= $this->actionMessage($oneRule); |
| 892 | 978 | |
| 893 | - $this->display($message); | |
| 979 | + $type = self::MESSAGE_TYPE_SUCCESS; | |
| 980 | + if ($this->errorDuringActions) { | |
| 981 | + $type = self::MESSAGE_TYPE_ERROR; | |
| 982 | + } | |
| 894 | 983 | |
| 984 | + $this->display($message, $type); | |
| 985 | + | |
| 895 | 986 | return true; |
| 896 | 987 | } |
| 897 | 988 | |
| 898 | 989 | private function actionUser(object &$oneRule): string |
| @@ -908,12 +999,14 @@ | ||
| 908 | 999 | |
| 909 | 1000 | return $message; |
| 910 | 1001 | } |
| 911 | 1002 | |
| 912 | - if (in_array('delete_user_subscription', $oneRule->action_user) || in_array('unsubscribe_user', $oneRule->action_user) || in_array( | |
| 913 | - 'subscribe_user', | |
| 914 | - $oneRule->action_user | |
| 915 | - )) { | |
| 1003 | + //To display nice error messages... | |
| 1004 | + if ( | |
| 1005 | + in_array('delete_user_subscription', $oneRule->action_user) | |
| 1006 | + || in_array('unsubscribe_user', $oneRule->action_user) | |
| 1007 | + || in_array('subscribe_user', $oneRule->action_user) | |
| 1008 | + ) { | |
| 916 | 1009 | if (empty($this->_message->subemail)) { |
| 917 | 1010 | $currentUser = $this->userClass->getOneById($this->_message->userid); |
| 918 | 1011 | if (!empty($currentUser->email)) { |
| 919 | 1012 | $this->_message->subemail = $currentUser->email; |
| @@ -950,11 +1043,17 @@ | ||
| 950 | 1043 | $this->_message->subemail = $this->_message->userid; |
| 951 | 1044 | } |
| 952 | 1045 | |
| 953 | 1046 | |
| 954 | - $mail = $this->mailClass->getOneById($this->_message->mailid); | |
| 1047 | + //handle this rule in the stats | |
| 1048 | + if (!empty($this->_message->mailid)) { | |
| 1049 | + $mail = $this->mailClass->getOneById($this->_message->mailid); | |
| 1050 | + } else { | |
| 1051 | + $mail = null; | |
| 1052 | + } | |
| 1053 | + | |
| 955 | 1054 | if ($oneRule->increment_stats && !empty($this->_message->mailid) && !empty($mail)) { |
| 956 | - | |
| 1055 | + //Init the stats... | |
| 957 | 1056 | if (empty($this->bounceMessages[$this->_message->mailid])) { |
| 958 | 1057 | $this->bounceMessages[$this->_message->mailid] = []; |
| 959 | 1058 | $this->bounceMessages[$this->_message->mailid]['nbbounces'] = 0; |
| 960 | 1059 | $this->bounceMessages[$this->_message->mailid]['userids'] = []; |
| @@ -961,11 +1060,14 @@ | ||
| 961 | 1060 | $this->bounceMessages[$this->_message->mailid]['bouncedetails'] = []; |
| 962 | 1061 | $this->bounceMessages[$this->_message->mailid]['ruletriggered'] = []; |
| 963 | 1062 | } |
| 964 | 1063 | |
| 1064 | + //Increment the global stats... | |
| 965 | 1065 | $this->bounceMessages[$this->_message->mailid]['nbbounces']++; |
| 966 | 1066 | |
| 1067 | + //Increment the detailed stats... | |
| 967 | 1068 | $ruleName = $oneRule->name.' [ID '.$oneRule->id.'] '; |
| 1069 | + //We add a @ just in case the rule was not already defined... | |
| 968 | 1070 | if (empty($this->bounceMessages[$this->_message->mailid]['bouncedetails'][$ruleName])) { |
| 969 | 1071 | $this->bounceMessages[$this->_message->mailid]['bouncedetails'][$ruleName] = 1; |
| 970 | 1072 | } else { |
| 971 | 1073 | $this->bounceMessages[$this->_message->mailid]['bouncedetails'][$ruleName]++; |
| @@ -972,8 +1074,9 @@ | ||
| 972 | 1074 | } |
| 973 | 1075 | |
| 974 | 1076 | $user = $this->userClass->getOneById($this->_message->userid); |
| 975 | 1077 | if (!empty($this->_message->userid) && !in_array('delete_user', $oneRule->action_user) && !empty($user)) { |
| 1078 | + //Increment the bounce number in the user stat table but only if we don't delete the subscriber | |
| 976 | 1079 | $this->bounceMessages[$this->_message->mailid]['userids'][] = intval($this->_message->userid); |
| 977 | 1080 | $this->bounceMessages[$this->_message->mailid]['ruletriggered'][intval($this->_message->userid)] = $oneRule->name.' ['.acym_translation( |
| 978 | 1081 | 'ACYM_ID' |
| 979 | 1082 | ).' '.$oneRule->id.']'; |
| @@ -979,9 +1082,11 @@ | ||
| 979 | 1082 | ).' '.$oneRule->id.']'; |
| 980 | 1083 | } |
| 981 | 1084 | } |
| 982 | 1085 | |
| 1086 | + //Make sure we have enough messages to really execute this | |
| 983 | 1087 | if (!empty($oneRule->execute_action_after) && $oneRule->execute_action_after > 1) { |
| 1088 | + //Let's load the number of bounces the user has and then exit or not... | |
| 984 | 1089 | if (empty($this->_message->mailid)) { |
| 985 | 1090 | $this->_message->mailid = 0; |
| 986 | 1091 | } |
| 987 | 1092 | $nb = intval( |
| @@ -1001,8 +1106,9 @@ | ||
| 1001 | 1106 | return $message; |
| 1002 | 1107 | } |
| 1003 | 1108 | } |
| 1004 | 1109 | |
| 1110 | + //If we delete the subscriber, it's the last action we execute | |
| 1005 | 1111 | if (in_array('delete_user', $oneRule->action_user)) { |
| 1006 | 1112 | $message .= ' | '.acym_translationSprintf('ACYM_USER_X_DELETED', $this->_message->subemail); |
| 1007 | 1113 | $this->deletedUsers[] = intval($this->_message->userid); |
| 1008 | 1114 | |
| @@ -1008,8 +1114,9 @@ | ||
| 1008 | 1114 | |
| 1009 | 1115 | return $message; |
| 1010 | 1116 | } |
| 1011 | 1117 | |
| 1118 | + //We will need a default listid after... | |
| 1012 | 1119 | $listId = 0; |
| 1013 | 1120 | if (in_array('subscribe_user', $oneRule->action_user) && !empty($oneRule->action_user['subscribe_user_list'])) { |
| 1014 | 1121 | $listId = $oneRule->action_user['subscribe_user_list']; |
| 1015 | 1122 | |
| @@ -1022,9 +1129,9 @@ | ||
| 1022 | 1129 | } elseif ($userSubscription[$listId]->status == 0) { |
| 1023 | 1130 | $message .= acym_translation('ACYM_USER_ALREADY_UNSUBSCRIBED'); |
| 1024 | 1131 | } |
| 1025 | 1132 | } else { |
| 1026 | - $this->userClass->subscribe($this->_message->userid, $listId); | |
| 1133 | + $this->userClass->subscribe([$this->_message->userid], [$listId]); | |
| 1027 | 1134 | $message .= acym_translationSprintf('ACYM_USER_X_SUBSCRIBED_TO', $this->_message->subemail, $listName); |
| 1028 | 1135 | } |
| 1029 | 1136 | } |
| 1030 | 1137 | |
| @@ -1039,9 +1146,9 @@ | ||
| 1039 | 1146 | $listNames[] = $oneListId; |
| 1040 | 1147 | } |
| 1041 | 1148 | } |
| 1042 | 1149 | $message .= ' | '.acym_translationSprintf('ACYM_USER_X_REMOVED_FROM', $this->_message->subemail, implode(', ', $listNames)); |
| 1043 | - $this->userClass->removeSubscription($this->_message->userid, $removeLists); | |
| 1150 | + $this->userClass->removeSubscription([$this->_message->userid], $removeLists); | |
| 1044 | 1151 | } else { |
| 1045 | 1152 | $message .= ' | '.acym_translationSprintf('ACYM_USER_X_NOT_SUBSCRIBED', $this->_message->subemail); |
| 1046 | 1153 | } |
| 1047 | 1154 | } |
| @@ -1056,9 +1163,9 @@ | ||
| 1056 | 1163 | } else { |
| 1057 | 1164 | $listNames[] = $oneListId; |
| 1058 | 1165 | } |
| 1059 | 1166 | } |
| 1060 | - $this->userClass->unsubscribe($this->_message->userid, $unsubLists); | |
| 1167 | + $this->userClass->unsubscribe([$this->_message->userid], $unsubLists); | |
| 1061 | 1168 | $message .= ' | '.acym_translationSprintf('ACYM_USER_X_UNSUBSCRIBED_FROM', $this->_message->subemail, implode(', ', $listNames)); |
| 1062 | 1169 | } else { |
| 1063 | 1170 | $message .= ' | '.acym_translationSprintf('ACYM_USER_X_NOT_SUBSCRIBED', $this->_message->subemail); |
| 1064 | 1171 | } |
| @@ -1084,27 +1191,35 @@ | ||
| 1084 | 1191 | private function actionMessage(object &$oneRule): string |
| 1085 | 1192 | { |
| 1086 | 1193 | $message = ''; |
| 1087 | 1194 | |
| 1195 | + //Fix the rule if needed... when the forwarded user is the same as the bounce e-mail address... | |
| 1088 | 1196 | if (!empty($oneRule->action_message['forward_to'])) { |
| 1089 | - if (strtolower($oneRule->action_message['forward_to']) == strtolower($this->config->get('bounce_username')) || strtolower( | |
| 1090 | - $oneRule->action_message['forward_to'] | |
| 1091 | - ) == strtolower($this->config->get('bounce_email'))) { | |
| 1197 | + if ( | |
| 1198 | + strtolower($oneRule->action_message['forward_to']) === strtolower($this->config->get('bounce_username')) | |
| 1199 | + || strtolower($oneRule->action_message['forward_to']) === strtolower($this->config->get('bounce_email')) | |
| 1200 | + ) { | |
| 1201 | + //We don't forward it | |
| 1092 | 1202 | $oneRule->action_message['forward_to'] = ''; |
| 1203 | + //We don't delete it | |
| 1093 | 1204 | unset($oneRule->action_message['delete_message']); |
| 1094 | 1205 | $message .= ' | '.acym_translation('ACYM_BOUNCE_NOT_FORWARD'); |
| 1095 | 1206 | } |
| 1096 | 1207 | } |
| 1097 | 1208 | |
| 1209 | + //Handle actions on the message itself | |
| 1098 | 1210 | |
| 1211 | + // We only delete the message if the forward didn't fail and the save in history succeeded | |
| 1099 | 1212 | $donotdelete = false; |
| 1100 | 1213 | |
| 1101 | 1214 | if (in_array('save_message', $oneRule->action_message) && !empty($this->_message->userid) && !in_array('delete_user', $oneRule->action_user)) { |
| 1215 | + //We have a userid, should we save the message in the database? | |
| 1102 | 1216 | $data = []; |
| 1103 | 1217 | $data[] = 'SUBJECT::'.@htmlentities($this->_message->subject, ENT_COMPAT, 'UTF-8'); |
| 1104 | 1218 | $data[] = 'ACY_RULE::'.$oneRule->id.' '.$oneRule->name; |
| 1105 | 1219 | $data[] = 'REPLYTO_ADDRESS::'.$this->_message->header->reply_to_name.' ( '.$this->_message->header->reply_to_email.' )'; |
| 1106 | 1220 | $data[] = 'FROM_ADDRESS::'.$this->_message->header->from_name.' ( '.$this->_message->header->from_email.' )'; |
| 1221 | + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Logging information. | |
| 1107 | 1222 | $data[] = @htmlentities(print_r($this->_message->headerinfo, true), ENT_COMPAT, 'UTF-8'); |
| 1108 | 1223 | if (empty($this->_message->mailid)) { |
| 1109 | 1224 | $this->_message->mailid = 0; |
| 1110 | 1225 | } |
| @@ -1123,9 +1238,11 @@ | ||
| 1123 | 1238 | } |
| 1124 | 1239 | } |
| 1125 | 1240 | |
| 1126 | 1241 | |
| 1242 | + //We don't forward the message if it's the same mailbox! | |
| 1127 | 1243 | if (!empty($oneRule->action_message['forward_to'])) { |
| 1244 | + //Get the forward address : | |
| 1128 | 1245 | $this->mailer->clearAll(); |
| 1129 | 1246 | $this->mailer->Subject = 'BOUNCE FORWARD : '.$this->_message->subject; |
| 1130 | 1247 | |
| 1131 | 1248 | if (substr_count($oneRule->action_message['forward_to'], '@') > 1) { |
| @@ -1137,8 +1254,9 @@ | ||
| 1137 | 1254 | foreach ($forwardAddresses as $oneForwardAddress) { |
| 1138 | 1255 | $this->mailer->AddAddress($this->mailer->cleanText($oneForwardAddress)); |
| 1139 | 1256 | } |
| 1140 | 1257 | |
| 1258 | + //Add the rule at the top so we know why the message has been forwarded. | |
| 1141 | 1259 | $info = acym_translation('ACYM_BOUNCE_RULE').' ['.acym_translation('ACYM_ID').' '.$oneRule->id.'] '.acym_translation($oneRule->name); |
| 1142 | 1260 | if (!empty($this->_message->html)) { |
| 1143 | 1261 | $this->mailer->isHTML(); |
| 1144 | 1262 | if (!empty($this->_message->userid)) { |
| @@ -1155,8 +1273,11 @@ | ||
| 1155 | 1273 | $this->mailer->isHTML(false); |
| 1156 | 1274 | $this->mailer->Body = $info."\n".$this->_message->text; |
| 1157 | 1275 | } |
| 1158 | 1276 | |
| 1277 | + //We add all other extra information just in case of we could use them... | |
| 1278 | + //original-rcpt-to ? http://tools.ietf.org/html/rfc5965 | |
| 1279 | + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Logging information. | |
| 1159 | 1280 | $this->mailer->Body .= print_r($this->_message->headerinfo, true); |
| 1160 | 1281 | $replyAddress = trim(@$this->_message->header->reply_to_email, '<> '); |
| 1161 | 1282 | if (!empty($replyAddress)) { |
| 1162 | 1283 | if ($this->config->get('mailer_method') === \plgAcymAcymailer::SENDING_METHOD_ID) { |
| @@ -1170,13 +1291,13 @@ | ||
| 1170 | 1291 | if ($this->mailer->send()) { |
| 1171 | 1292 | $message .= ' | '.acym_translationSprintf('ACYM_FORWARDED_TO_X', $oneRule->action_message['forward_to']); |
| 1172 | 1293 | } else { |
| 1173 | 1294 | $message .= ' | '.acym_translationSprintf('ACYM_NOT_FORWARDED_TO_X', $oneRule->action_message['forward_to'], $this->mailer->reportMessage); |
| 1174 | - $donotdelete = true; | |
| 1295 | + $this->errorDuringActions = true; | |
| 1175 | 1296 | } |
| 1176 | 1297 | } |
| 1177 | 1298 | |
| 1178 | - if (in_array('delete_message', $oneRule->action_message) && !$donotdelete) { | |
| 1299 | + if (in_array('delete_message', $oneRule->action_message) && !$this->errorDuringActions) { | |
| 1179 | 1300 | $message .= ' | '.acym_translation('ACYM_MESSAGE_DELETED'); |
| 1180 | 1301 | $this->deleteMessage($this->_message->messageNB); |
| 1181 | 1302 | } |
| 1182 | 1303 | |
| @@ -1206,22 +1327,32 @@ | ||
| 1206 | 1327 | |
| 1207 | 1328 | $this->_message->header->$email = $var[0]->mailbox.'@'.@$var[0]->host; |
| 1208 | 1329 | } |
| 1209 | 1330 | |
| 1210 | - protected function display(string $message, bool $success = true, string $num = ''): void | |
| 1331 | + /** | |
| 1332 | + * If num is empty then it's a message otherwise it's a send status | |
| 1333 | + */ | |
| 1334 | + protected function display(string $message, int $type = self::MESSAGE_TYPE_SUCCESS, string $num = ''): void | |
| 1211 | 1335 | { |
| 1212 | 1336 | $this->messages[] = $message; |
| 1213 | 1337 | |
| 1214 | 1338 | if (!$this->report) return; |
| 1215 | 1339 | |
| 1216 | - $color = $success ? 'green' : 'blue'; | |
| 1340 | + if ($type === self::MESSAGE_TYPE_SUCCESS) { | |
| 1341 | + $color = 'green'; | |
| 1342 | + } elseif ($type === self::MESSAGE_TYPE_INFO) { | |
| 1343 | + $color = 'blue'; | |
| 1344 | + } else { | |
| 1345 | + $color = 'red'; | |
| 1346 | + } | |
| 1347 | + | |
| 1217 | 1348 | if (!empty($num)) { |
| 1218 | - echo '<br />'.$num.' : '; | |
| 1349 | + echo '<br />'.esc_html($num).' : '; | |
| 1219 | 1350 | } else { |
| 1220 | 1351 | echo '<br /> '; |
| 1221 | 1352 | } |
| 1222 | 1353 | |
| 1223 | - echo '<span style="color: '.$color.'">'.$message.'</span>'; | |
| 1354 | + echo '<span style="color: '.esc_attr($color).'">'.esc_html($message).'</span>'; | |
| 1224 | 1355 | |
| 1225 | 1356 | if (function_exists('ob_flush')) { |
| 1226 | 1357 | @ob_flush(); |
| 1227 | 1358 | } |
| @@ -1231,13 +1362,14 @@ | ||
| 1231 | 1362 | } |
| 1232 | 1363 | |
| 1233 | 1364 | public function decodeHeader(string $input): string |
| 1234 | 1365 | { |
| 1366 | + // Remove white space between encoded-words | |
| 1235 | 1367 | $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); |
| 1236 | 1368 | $currentCharset = false; |
| 1237 | 1369 | |
| 1370 | + // For each encoded-word... | |
| 1238 | 1371 | while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { |
| 1239 | - | |
| 1240 | 1372 | $encoded = $matches[1]; |
| 1241 | 1373 | $charset = $matches[2]; |
| 1242 | 1374 | $encoding = $matches[3]; |
| 1243 | 1375 | $text = $matches[4]; |
| @@ -1258,8 +1390,9 @@ | ||
| 1258 | 1390 | $currentCharset = $charset; |
| 1259 | 1391 | $input = str_replace($encoded, $text, $input); |
| 1260 | 1392 | } |
| 1261 | 1393 | |
| 1394 | + //If we have a charset and we can handle it... | |
| 1262 | 1395 | if (!empty($currentCharset) && in_array($currentCharset, $this->allCharsets)) { |
| 1263 | 1396 | $input = $this->encodingHelper->change($input, $currentCharset, 'UTF-8'); |
| 1264 | 1397 | } |
| 1265 | 1398 | |
| @@ -1294,16 +1427,22 @@ | ||
| 1294 | 1427 | if (empty($struct->parts)) { |
| 1295 | 1428 | return $allParts; |
| 1296 | 1429 | } |
| 1297 | 1430 | |
| 1298 | - $c = 0; //counts real content | |
| 1431 | + //counts real content | |
| 1432 | + $c = 0; | |
| 1299 | 1433 | foreach ($struct->parts as $part) { |
| 1300 | 1434 | if ($part->type == 1) { |
| 1301 | - if ($part->subtype === 'MIXED') { //Mixed: | |
| 1302 | - $path = $this->incPath($path); //refreshing current path | |
| 1303 | - $newpath = $path.'.0'; //create a new path-id (ex.:2.0) | |
| 1304 | - $allParts = array_merge($this->explodeBody($part, $newpath), $allParts); //fetch new parts | |
| 1305 | - } else { //Alternativ / rfc / signed | |
| 1435 | + //There are more parts....: | |
| 1436 | + if ($part->subtype === 'MIXED') { | |
| 1437 | + // refreshing current path | |
| 1438 | + $path = $this->incPath($path); | |
| 1439 | + //create a new path-id (ex.:2.0) | |
| 1440 | + $newpath = $path.'.0'; | |
| 1441 | + //fetch new parts | |
| 1442 | + $allParts = array_merge($this->explodeBody($part, $newpath), $allParts); | |
| 1443 | + } else { | |
| 1444 | + //Alternativ / rfc / signed | |
| 1306 | 1445 | $newpath = $this->incPath($path); |
| 1307 | 1446 | $path = $this->incPath($path); |
| 1308 | 1447 | $allParts = array_merge($this->explodeBody($part, $newpath, true), $allParts); |
| 1309 | 1448 | } |
| @@ -1308,12 +1447,15 @@ | ||
| 1308 | 1447 | $allParts = array_merge($this->explodeBody($part, $newpath, true), $allParts); |
| 1309 | 1448 | } |
| 1310 | 1449 | } else { |
| 1311 | 1450 | $c++; |
| 1451 | + //creating new tree if this is part of a alternativ or rfc message: | |
| 1312 | 1452 | if ($c == 1 && $inline) { |
| 1313 | 1453 | $path = $path.'.0'; |
| 1314 | 1454 | } |
| 1455 | + //saving content: | |
| 1315 | 1456 | $path = $this->incPath($path); |
| 1457 | + //print "<br> Content ".$path."<br>"; //debug information | |
| 1316 | 1458 | $allParts[$path] = $part; |
| 1317 | 1459 | } |
| 1318 | 1460 | } |
| 1319 | 1461 | |
| @@ -1319,18 +1461,22 @@ | ||
| 1319 | 1461 | |
| 1320 | 1462 | return $allParts; |
| 1321 | 1463 | } |
| 1322 | 1464 | |
| 1465 | + //Increases the Path to the parts: | |
| 1323 | 1466 | private function incPath(string $path): string |
| 1324 | 1467 | { |
| 1325 | 1468 | $newPath = ''; |
| 1326 | 1469 | $path_elements = explode('.', $path); |
| 1327 | 1470 | $limit = count($path_elements); |
| 1328 | - for ($i = 0 ; $i < $limit ; $i++) { | |
| 1329 | - if ($i == $limit - 1) { //last element | |
| 1330 | - $newPath .= $path_elements[$i] + 1; // new Part-Number | |
| 1471 | + for ($i = 0; $i < $limit; $i++) { | |
| 1472 | + //last element | |
| 1473 | + if ($i == $limit - 1) { | |
| 1474 | + // new Part-Number | |
| 1475 | + $newPath .= $path_elements[$i] + 1; | |
| 1331 | 1476 | } else { |
| 1332 | - $newPath .= $path_elements[$i]."."; //rebuild "1.2.2"-Chronology | |
| 1477 | + //rebuild "1.2.2"-Chronology | |
| 1478 | + $newPath .= $path_elements[$i].'.'; | |
| 1333 | 1479 | } |
| 1334 | 1480 | } |
| 1335 | 1481 | |
| 1336 | 1482 | return $newPath; |
| @@ -1339,8 +1485,9 @@ | ||
| 1339 | 1485 | private function decodeContent(string $content, object $structure): string |
| 1340 | 1486 | { |
| 1341 | 1487 | $encoding = $structure->encoding; |
| 1342 | 1488 | |
| 1489 | + //First we decode the content properly | |
| 1343 | 1490 | if ($encoding == 2) { |
| 1344 | 1491 | $content = imap_binary($content); |
| 1345 | 1492 | } elseif ($encoding == 3) { |
| 1346 | 1493 | $content = base64_decode($content, true); |
| @@ -1347,8 +1494,9 @@ | ||
| 1347 | 1494 | } elseif ($encoding == 4) { |
| 1348 | 1495 | $content = imap_qprint($content); |
| 1349 | 1496 | } |
| 1350 | 1497 | |
| 1498 | + // Now we convert into utf-8! only for distribution lists | |
| 1351 | 1499 | if (!empty($this->action)) { |
| 1352 | 1500 | $charset = $this->getMailParam($structure); |
| 1353 | 1501 | if (!empty($charset) && strtoupper($charset) !== 'UTF-8') { |
| 1354 | 1502 | $content = $this->encodingHelper->change($content, $charset, 'UTF-8'); |
| @@ -1356,8 +1504,9 @@ | ||
| 1356 | 1504 | |
| 1357 | 1505 | return $content; |
| 1358 | 1506 | } |
| 1359 | 1507 | |
| 1508 | + //It can't be more than 100 000 characters, it's already plenty enough for bounce handling... we avoid embedded pictures or big attachments we could not catch before. | |
| 1360 | 1509 | return substr($content, 0, 100000); |
| 1361 | 1510 | } |
| 1362 | 1511 | |
| 1363 | 1512 | private function getMailParam(object $params): string |
| @@ -1407,52 +1556,34 @@ | ||
| 1407 | 1556 | } |
| 1408 | 1557 | |
| 1409 | 1558 | private function refreshToken(): void |
| 1410 | 1559 | { |
| 1411 | - if ((int)$this->config->get('bounce_token_expireIn') > time()) { | |
| 1412 | - return; | |
| 1413 | - } | |
| 1414 | - | |
| 1415 | 1560 | if ($this->config->get('bounce_server') === 'imap.gmail.com') { |
| 1416 | 1561 | $url = 'https://oauth2.googleapis.com/token'; |
| 1417 | 1562 | } else { |
| 1418 | - $tenant = $this->config->get('bounce_tenant'); | |
| 1419 | - if (empty($tenant)) { | |
| 1420 | - acym_enqueueMessage(acym_translation('ACYM_TENANT_FIELD_IS_MISSING'), 'error'); | |
| 1421 | - } | |
| 1563 | + $tenant = $this->config->get('bounce_tenant', 'consumers'); | |
| 1422 | 1564 | $url = 'https://login.microsoftonline.com/'.$tenant.'/oauth2/v2.0/token'; |
| 1423 | 1565 | } |
| 1424 | 1566 | |
| 1425 | - $requestOptions = [ | |
| 1426 | - 'method' => 'POST', | |
| 1427 | - 'data' => [ | |
| 1428 | - 'client_id' => $this->config->get('bounce_client_id'), | |
| 1429 | - 'grant_type' => 'refresh_token', | |
| 1430 | - 'refresh_token' => $this->config->get('bounce_refresh_token'), | |
| 1431 | - 'client_secret' => $this->config->get('bounce_client_secret'), | |
| 1432 | - ], | |
| 1433 | - ]; | |
| 1434 | - $response = acym_makeCurlCall( | |
| 1435 | - $url, | |
| 1436 | - $requestOptions | |
| 1567 | + $oauth = new OAuth( | |
| 1568 | + [ | |
| 1569 | + 'sendingMethod' => 'bounce', | |
| 1570 | + 'tokenGenerationUrl' => $url, | |
| 1571 | + 'userName' => trim($this->config->get('bounce_username')), | |
| 1572 | + 'clientId' => trim($this->config->get('bounce_client_id')), | |
| 1573 | + 'clientSecret' => trim($this->config->get('bounce_client_secret')), | |
| 1574 | + 'oauthToken' => trim($this->config->get('bounce_access_token')), | |
| 1575 | + 'oauthTokenExpiration' => trim($this->config->get('bounce_access_token_expiration')), | |
| 1576 | + 'refreshToken' => trim($this->config->get('bounce_refresh_token')), | |
| 1577 | + ] | |
| 1437 | 1578 | ); |
| 1438 | 1579 | |
| 1439 | - acym_logError('Response from OAuth refresh token call: '.json_encode($response), 'imap_oauth', 100); | |
| 1440 | - | |
| 1441 | - if (empty($response['error'])) { | |
| 1442 | - $token = $response['token_type'].' '.$response['access_token']; | |
| 1443 | - $expireIn = time() + (int)$response['expires_in']; | |
| 1444 | - $this->config->save(['bounce_token' => $token, 'bounce_token_expireIn' => $expireIn]); | |
| 1445 | - | |
| 1446 | - $this->oAuthToken = $token; | |
| 1447 | - } else { | |
| 1448 | - acym_enqueueMessage(acym_translationSprintf('ACYM_OAUTH_REFRESH_TOKEN_ERROR', $response['error']), 'error'); | |
| 1449 | - } | |
| 1580 | + $this->oAuthToken = $oauth->getToken(); | |
| 1450 | 1581 | } |
| 1451 | 1582 | |
| 1452 | 1583 | private function callImapFunction(string $functionName, array $params = []) |
| 1453 | 1584 | { |
| 1454 | - if ($this->imapConnectionMethod === 'oauth') { | |
| 1585 | + if (in_array($this->server, self::HOSTS_NEEDING_OAUTH)) { | |
| 1455 | 1586 | $functionName = str_replace('imap_', 'imap2_', $functionName); |
| 1456 | 1587 | } |
| 1457 | 1588 | |
| 1458 | 1589 | if (empty($params)) { |