YetiForceCompany /
YetiForceCRM
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace App; |
||||
| 4 | |||||
| 5 | /** |
||||
| 6 | * Mailer basic class. |
||||
| 7 | * |
||||
| 8 | * @package App |
||||
| 9 | * |
||||
| 10 | * @copyright YetiForce S.A. |
||||
| 11 | * @license YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com) |
||||
| 12 | * @author Mariusz Krzaczkowski <[email protected]> |
||||
| 13 | * @author Radosław Skrzypczak <[email protected]> |
||||
| 14 | */ |
||||
| 15 | class Mailer |
||||
| 16 | { |
||||
| 17 | /** @var string[] Queue status */ |
||||
| 18 | public static $statuses = [ |
||||
| 19 | 0 => 'LBL_PENDING_ACCEPTANCE', |
||||
| 20 | 1 => 'LBL_WAITING_TO_BE_SENT', |
||||
| 21 | 2 => 'LBL_ERROR_DURING_SENDING', |
||||
| 22 | ]; |
||||
| 23 | |||||
| 24 | /** @var string[] Columns list that require JSON formatting */ |
||||
| 25 | public static $quoteJsonColumn = ['from', 'to', 'cc', 'bcc', 'attachments', 'params']; |
||||
| 26 | |||||
| 27 | /** @var string[] Columns list available in the database */ |
||||
| 28 | public static $quoteColumn = ['smtp_id', 'date', 'owner', 'status', 'from', 'subject', 'content', 'to', 'cc', 'bcc', 'attachments', 'priority']; |
||||
| 29 | |||||
| 30 | /** @var \PHPMailer\PHPMailer\PHPMailer PHPMailer instance */ |
||||
| 31 | protected $mailer; |
||||
| 32 | |||||
| 33 | /** @var array SMTP configuration */ |
||||
| 34 | protected $smtp; |
||||
| 35 | |||||
| 36 | /** @var array Parameters for sending messages */ |
||||
| 37 | protected $params = []; |
||||
| 38 | |||||
| 39 | /** @var array Error logs */ |
||||
| 40 | public static $error; |
||||
| 41 | |||||
| 42 | /** |
||||
| 43 | * Construct. |
||||
| 44 | */ |
||||
| 45 | public function __construct() |
||||
| 46 | { |
||||
| 47 | static::$error = []; |
||||
| 48 | $this->mailer = new \PHPMailer\PHPMailer\PHPMailer(false); |
||||
| 49 | if (\App\Config::debug('MAILER_DEBUG')) { |
||||
| 50 | $this->mailer->SMTPDebug = 2; |
||||
| 51 | $this->mailer->Debugoutput = function ($str, $level) { |
||||
| 52 | if (false !== stripos($str, 'error') || false !== stripos($str, 'failed')) { |
||||
| 53 | static::$error[] = $str; |
||||
| 54 | Log::error(trim($str), 'Mailer'); |
||||
| 55 | } else { |
||||
| 56 | Log::trace(trim($str), 'Mailer'); |
||||
| 57 | } |
||||
| 58 | }; |
||||
| 59 | } |
||||
| 60 | $this->mailer->XMailer = 'YetiForceCRM Mailer'; |
||||
| 61 | $this->mailer->Hostname = 'YetiForceCRM'; |
||||
| 62 | $this->mailer->FromName = 'YetiForce Mailer'; |
||||
| 63 | $this->mailer->CharSet = \App\Config::main('default_charset'); |
||||
| 64 | } |
||||
| 65 | |||||
| 66 | /** |
||||
| 67 | * Load configuration smtp by id. |
||||
| 68 | * |
||||
| 69 | * @param int $smtpId Smtp ID |
||||
| 70 | * |
||||
| 71 | * @return $this mailer object itself |
||||
| 72 | */ |
||||
| 73 | public function loadSmtpByID($smtpId) |
||||
| 74 | { |
||||
| 75 | $this->smtp = Mail::getSmtpById($smtpId); |
||||
| 76 | $this->setSmtp(); |
||||
| 77 | return $this; |
||||
| 78 | } |
||||
| 79 | |||||
| 80 | /** |
||||
| 81 | * Load configuration smtp. |
||||
| 82 | * |
||||
| 83 | * @param array $smtpInfo |
||||
| 84 | * |
||||
| 85 | * @return $this mailer object itself |
||||
| 86 | */ |
||||
| 87 | public function loadSmtp($smtpInfo) |
||||
| 88 | 1 | { |
|||
| 89 | $this->smtp = $smtpInfo; |
||||
| 90 | 1 | $this->setSmtp(); |
|||
| 91 | 1 | return $this; |
|||
| 92 | } |
||||
| 93 | |||||
| 94 | /** |
||||
| 95 | 1 | * @param array $params |
|||
| 96 | 1 | * |
|||
| 97 | 1 | * @return bool |
|||
| 98 | 1 | */ |
|||
| 99 | public static function sendFromTemplate(array $params): bool |
||||
| 100 | { |
||||
| 101 | Log::trace('Send mail from template', 'Mailer'); |
||||
| 102 | if (empty($params['template'])) { |
||||
| 103 | Log::warning('No template', 'Mailer'); |
||||
| 104 | 1 | return false; |
|||
| 105 | 1 | } |
|||
| 106 | $recordModel = false; |
||||
| 107 | if (empty($params['recordModel'])) { |
||||
| 108 | $moduleName = $params['moduleName'] ?? null; |
||||
| 109 | 1 | if (isset($params['recordId'])) { |
|||
| 110 | 1 | $recordModel = \Vtiger_Record_Model::getInstanceById($params['recordId'], $moduleName); |
|||
| 111 | } |
||||
| 112 | } else { |
||||
| 113 | 1 | $recordModel = $params['recordModel']; |
|||
| 114 | unset($params['recordModel']); |
||||
| 115 | } |
||||
| 116 | 1 | $template = Mail::getTemplate($params['template']); |
|||
| 117 | 1 | if (!$template) { |
|||
| 118 | 1 | Log::warning('No mail template', 'Mailer'); |
|||
| 119 | 1 | return false; |
|||
| 120 | 1 | } |
|||
| 121 | $textParser = $recordModel ? TextParser::getInstanceByModel($recordModel) : TextParser::getInstance($params['moduleName'] ?? ''); |
||||
| 122 | if (!empty($params['language'])) { |
||||
| 123 | 1 | $textParser->setLanguage($params['language']); |
|||
| 124 | } |
||||
| 125 | if (!empty($params['sourceRecord'])) { |
||||
| 126 | 1 | $textParser->setSourceRecord($params['sourceRecord'], $params['sourceModule']); |
|||
| 127 | 1 | } |
|||
| 128 | $textParser->setParams(array_diff_key($params, array_flip(['subject', 'content', 'attachments', 'recordModel']))); |
||||
| 129 | 1 | $params['subject'] = $textParser->setContent($template['subject'])->parse()->getContent(); |
|||
| 130 | $params['content'] = $textParser->setContent(\App\Utils\Completions::decode(\App\Purifier::purifyHtml($template['content'])))->parse()->getContent(); |
||||
| 131 | unset($textParser); |
||||
| 132 | if (empty($params['smtp_id']) && !empty($template['smtp_id'])) { |
||||
| 133 | $params['smtp_id'] = $template['smtp_id']; |
||||
| 134 | } |
||||
| 135 | if (isset($template['attachments'])) { |
||||
| 136 | $params['attachments'] = array_merge(empty($params['attachments']) ? [] : $params['attachments'], $template['attachments']); |
||||
| 137 | 1 | } |
|||
| 138 | if (!empty($template['email_template_priority'])) { |
||||
| 139 | 1 | $params['priority'] = $template['email_template_priority']; |
|||
| 140 | 1 | } |
|||
| 141 | 1 | $row = array_intersect_key($params, array_flip(self::$quoteColumn)); |
|||
| 142 | 1 | $row['params'] = array_diff_key($params, $row); |
|||
| 143 | 1 | return static::addMail($row); |
|||
| 144 | } |
||||
| 145 | 1 | ||||
| 146 | 1 | /** |
|||
| 147 | * Add mail to quote for send. |
||||
| 148 | 1 | * |
|||
| 149 | 1 | * @param array $params |
|||
| 150 | 1 | * |
|||
| 151 | 1 | * @return bool |
|||
| 152 | 1 | */ |
|||
| 153 | 1 | public static function addMail(array $params): bool |
|||
| 154 | { |
||||
| 155 | $params['status'] = Config::component('Mail', 'MAILER_REQUIRED_ACCEPTATION_BEFORE_SENDING') ? 0 : 1; |
||||
| 156 | $params['date'] = date('Y-m-d H:i:s'); |
||||
| 157 | if (empty($params['owner'])) { |
||||
| 158 | $owner = User::getCurrentUserRealId(); |
||||
| 159 | $params['owner'] = $owner ?: 0; |
||||
| 160 | } |
||||
| 161 | if (empty($params['smtp_id'])) { |
||||
| 162 | $params['smtp_id'] = Mail::getDefaultSmtp(); |
||||
| 163 | } |
||||
| 164 | if (empty($params['smtp_id'])) { |
||||
| 165 | unset($params['priority'], $params['status']); |
||||
| 166 | $params['error_code'] = 1; |
||||
| 167 | static::insertMail($params, 'log'); |
||||
| 168 | Log::warning('No SMTP configuration', 'Mailer'); |
||||
| 169 | return false; |
||||
| 170 | } |
||||
| 171 | if (!\App\Mail::getSmtpById($params['smtp_id'])) { |
||||
| 172 | unset($params['priority'], $params['status']); |
||||
| 173 | $params['error_code'] = 2; |
||||
| 174 | static::insertMail($params, 'log'); |
||||
| 175 | Log::warning('SMTP configuration with provided id not exists', 'Mailer'); |
||||
| 176 | return false; |
||||
| 177 | } |
||||
| 178 | if (empty($params['to'])) { |
||||
| 179 | unset($params['priority'], $params['status']); |
||||
| 180 | $params['error_code'] = 3; |
||||
| 181 | 1 | static::insertMail($params, 'log'); |
|||
| 182 | Log::warning('No target email address provided', 'Mailer'); |
||||
| 183 | 1 | return false; |
|||
| 184 | 1 | } |
|||
| 185 | 1 | static::insertMail($params, 'admin'); |
|||
| 186 | return true; |
||||
| 187 | } |
||||
| 188 | 1 | ||||
| 189 | /** |
||||
| 190 | * Save mail data in provided table. |
||||
| 191 | 1 | * |
|||
| 192 | 1 | * @param array $params |
|||
| 193 | * @param string $type 'admin' | 'log' |
||||
| 194 | * |
||||
| 195 | * @return void |
||||
| 196 | */ |
||||
| 197 | public static function insertMail(array $params, string $type): void |
||||
| 198 | { |
||||
| 199 | $eventHandler = new EventHandler(); |
||||
| 200 | $eventHandler->setParams($params); |
||||
| 201 | $eventHandler->trigger('admin' === $type ? 'MailerAddToQueue' : 'MailerAddToLogs'); |
||||
| 202 | $params = $eventHandler->getParams(); |
||||
| 203 | |||||
| 204 | foreach (static::$quoteJsonColumn as $key) { |
||||
| 205 | if (isset($params[$key])) { |
||||
| 206 | if (!\is_array($params[$key])) { |
||||
| 207 | $params[$key] = [$params[$key]]; |
||||
| 208 | } |
||||
| 209 | $params[$key] = Json::encode($params[$key]); |
||||
| 210 | } |
||||
| 211 | } |
||||
| 212 | \App\Db::getInstance($type)->createCommand()->insert('admin' === $type ? 's_#__mail_queue' : 'l_#__mail', $params)->execute(); |
||||
| 213 | } |
||||
| 214 | |||||
| 215 | /** |
||||
| 216 | * Get configuration smtp. |
||||
| 217 | * |
||||
| 218 | * @param string|null $key |
||||
| 219 | * |
||||
| 220 | * @return mixed |
||||
| 221 | */ |
||||
| 222 | public function getSmtp(?string $key = null) |
||||
| 223 | { |
||||
| 224 | if ($key && isset($this->smtp[$key])) { |
||||
| 225 | return $this->smtp[$key]; |
||||
| 226 | } |
||||
| 227 | return $this->smtp; |
||||
| 228 | } |
||||
| 229 | |||||
| 230 | /** |
||||
| 231 | * Set configuration smtp in mailer. |
||||
| 232 | */ |
||||
| 233 | public function setSmtp(): void |
||||
| 234 | { |
||||
| 235 | if (!$this->smtp) { |
||||
|
0 ignored issues
–
show
|
|||||
| 236 | throw new \App\Exceptions\AppException('ERR_NO_SMTP_CONFIGURATION'); |
||||
| 237 | } |
||||
| 238 | switch ($this->smtp['mailer_type']) { |
||||
| 239 | case 'smtp': |
||||
| 240 | $this->mailer->isSMTP(); |
||||
| 241 | break; |
||||
| 242 | case 'sendmail': |
||||
| 243 | $this->mailer->isSendmail(); |
||||
| 244 | break; |
||||
| 245 | case 'mail': |
||||
| 246 | $this->mailer->isMail(); |
||||
| 247 | break; |
||||
| 248 | case 'qmail': |
||||
| 249 | $this->mailer->isQmail(); |
||||
| 250 | break; |
||||
| 251 | default: |
||||
| 252 | break; |
||||
| 253 | } |
||||
| 254 | $this->mailer->Host = $this->smtp['host']; |
||||
| 255 | if (!empty($this->smtp['port'])) { |
||||
| 256 | $this->mailer->Port = $this->smtp['port']; |
||||
| 257 | } |
||||
| 258 | $this->mailer->SMTPSecure = $this->smtp['secure']; |
||||
| 259 | $this->mailer->SMTPAuth = isset($this->smtp['authentication']) && (bool) $this->smtp['authentication']; |
||||
| 260 | $this->mailer->Username = trim($this->smtp['username']); |
||||
| 261 | $this->mailer->Password = trim(Encryption::getInstance()->decrypt($this->smtp['password'])); |
||||
| 262 | if ($this->smtp['options']) { |
||||
| 263 | $this->mailer->SMTPOptions = Json::decode($this->smtp['options'], true); |
||||
|
0 ignored issues
–
show
true of type true is incompatible with the type integer expected by parameter $objectDecodeType of App\Json::decode().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
App\Json::decode($this->smtp['options'], true) can also be of type string. However, the property $SMTPOptions is declared as type array. Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
Loading history...
|
|||||
| 264 | } |
||||
| 265 | $this->mailer->From = $this->smtp['from_email'] ?: $this->smtp['username']; |
||||
| 266 | if ($this->smtp['from_name']) { |
||||
| 267 | $this->mailer->FromName = $this->smtp['from_name']; |
||||
| 268 | } |
||||
| 269 | if ($this->smtp['reply_to']) { |
||||
| 270 | $this->mailer->addReplyTo($this->smtp['reply_to']); |
||||
| 271 | } |
||||
| 272 | if ($this->smtp['unsubscribe']) { |
||||
| 273 | $unsubscribe = ''; |
||||
| 274 | foreach (\App\Json::decode($this->smtp['unsubscribe']) as $row) { |
||||
| 275 | $unsubscribe .= "<$row>,"; |
||||
| 276 | } |
||||
| 277 | $unsubscribe = rtrim($unsubscribe, ','); |
||||
| 278 | $this->mailer->AddCustomHeader('List-Unsubscribe', $unsubscribe); |
||||
| 279 | } |
||||
| 280 | if ($this->smtp['priority']) { |
||||
| 281 | $priorityName = $priority = $priorityX = null; |
||||
| 282 | switch ($this->smtp['priority']) { |
||||
| 283 | case 'normal': |
||||
| 284 | case 'Normal': |
||||
| 285 | $priorityX = 3; |
||||
| 286 | $priority = $priorityName = 'Normal'; |
||||
| 287 | break; |
||||
| 288 | case 'non-urgent': |
||||
| 289 | case 'Low': |
||||
| 290 | $priorityX = 5; |
||||
| 291 | $priority = 'Non-Urgent'; |
||||
| 292 | $priorityName = 'Low'; |
||||
| 293 | break; |
||||
| 294 | case 'urgent': |
||||
| 295 | case 'High': |
||||
| 296 | $priorityX = 1; |
||||
| 297 | $priority = 'Urgent'; |
||||
| 298 | $priorityName = 'High'; |
||||
| 299 | break; |
||||
| 300 | } |
||||
| 301 | if ($priority) { |
||||
| 302 | $this->mailer->Priority = $priorityX; |
||||
| 303 | $this->mailer->AddCustomHeader('Priority', $priority); |
||||
| 304 | $this->mailer->AddCustomHeader('X-MSMail-Priority', $priorityName); |
||||
| 305 | $this->mailer->AddCustomHeader('Importance', $priorityName); |
||||
| 306 | } |
||||
| 307 | } |
||||
| 308 | if ($this->smtp['confirm_reading_to']) { |
||||
| 309 | $this->mailer->ConfirmReadingTo = $this->smtp['confirm_reading_to']; |
||||
| 310 | } |
||||
| 311 | if ($this->smtp['organization']) { |
||||
| 312 | $this->mailer->AddCustomHeader('Organization', $this->smtp['organization']); |
||||
| 313 | } |
||||
| 314 | } |
||||
| 315 | |||||
| 316 | /** |
||||
| 317 | * Set subject. |
||||
| 318 | * |
||||
| 319 | * @param string $subject |
||||
| 320 | * |
||||
| 321 | * @return $this mailer object itself |
||||
| 322 | */ |
||||
| 323 | public function subject($subject) |
||||
| 324 | { |
||||
| 325 | $this->params['subject'] = $this->mailer->Subject = $subject; |
||||
| 326 | return $this; |
||||
| 327 | } |
||||
| 328 | |||||
| 329 | /** |
||||
| 330 | * Creates a message from an HTML string, making modifications for inline images and backgrounds and creates a plain-text version by converting the HTML. |
||||
| 331 | * |
||||
| 332 | * @param text $message |
||||
| 333 | * |
||||
| 334 | * @see \PHPMailer::MsgHTML() |
||||
| 335 | * |
||||
| 336 | * @return $this mailer object itself |
||||
| 337 | */ |
||||
| 338 | public function content($message) |
||||
| 339 | { |
||||
| 340 | $this->params['body'] = $message; |
||||
| 341 | // Modification of the following condition will violate the license! |
||||
| 342 | if (!\App\YetiForce\Shop::check('YetiForceDisableBranding')) { |
||||
| 343 | $message .= '<table style="font-size:9px;width:100%; margin: 0;"><tbody><tr><td style="width:50%;text-align: center;">Powered by YetiForce</td></tr></tbody></table>'; |
||||
| 344 | } |
||||
| 345 | $this->mailer->isHTML(true); |
||||
| 346 | $this->mailer->msgHTML($message); |
||||
| 347 | return $this; |
||||
| 348 | } |
||||
| 349 | |||||
| 350 | /** |
||||
| 351 | * Set the From and FromName properties. |
||||
| 352 | * |
||||
| 353 | * @param string $address |
||||
| 354 | * @param string $name |
||||
| 355 | * |
||||
| 356 | * @return $this mailer object itself |
||||
| 357 | */ |
||||
| 358 | public function from($address, $name = '') |
||||
| 359 | { |
||||
| 360 | $this->params['from'][$address] = $name; |
||||
| 361 | $this->mailer->From = $address; |
||||
| 362 | $this->mailer->FromName = $name; |
||||
| 363 | return $this; |
||||
| 364 | } |
||||
| 365 | |||||
| 366 | /** |
||||
| 367 | * Add a "To" address. |
||||
| 368 | * |
||||
| 369 | * @param string $address The email address to send to |
||||
| 370 | * @param string $name |
||||
| 371 | * |
||||
| 372 | * @return $this mailer object itself |
||||
| 373 | */ |
||||
| 374 | public function to($address, $name = '') |
||||
| 375 | { |
||||
| 376 | $this->params['to'][$address] = $name; |
||||
| 377 | $this->mailer->addAddress($address, $name); |
||||
| 378 | return $this; |
||||
| 379 | } |
||||
| 380 | |||||
| 381 | /** |
||||
| 382 | * Add a "CC" address. |
||||
| 383 | * |
||||
| 384 | * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. |
||||
| 385 | * |
||||
| 386 | * @param string $address The email address to send to |
||||
| 387 | * @param string $name |
||||
| 388 | * |
||||
| 389 | * @return $this mailer object itself |
||||
| 390 | */ |
||||
| 391 | public function cc($address, $name = '') |
||||
| 392 | { |
||||
| 393 | $this->params['cc'][$address] = $name; |
||||
| 394 | $this->mailer->addCC($address, $name); |
||||
| 395 | return $this; |
||||
| 396 | } |
||||
| 397 | |||||
| 398 | /** |
||||
| 399 | * Add a "BCC" address. |
||||
| 400 | * |
||||
| 401 | * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. |
||||
| 402 | * |
||||
| 403 | * @param string $address The email address to send to |
||||
| 404 | * @param string $name |
||||
| 405 | * |
||||
| 406 | * @return $this mailer object itself |
||||
| 407 | */ |
||||
| 408 | public function bcc($address, $name = '') |
||||
| 409 | { |
||||
| 410 | $this->params['bcc'][$address] = $name; |
||||
| 411 | $this->mailer->addBCC($address, $name); |
||||
| 412 | return $this; |
||||
| 413 | } |
||||
| 414 | |||||
| 415 | /** |
||||
| 416 | * Add a "Reply-To" address. |
||||
| 417 | * |
||||
| 418 | * @param string $address The email address to reply to |
||||
| 419 | * @param string $name |
||||
| 420 | * |
||||
| 421 | * @return $this mailer object itself |
||||
| 422 | */ |
||||
| 423 | public function replyTo($address, $name = '') |
||||
| 424 | { |
||||
| 425 | $this->params['replyTo'][$address] = $name; |
||||
| 426 | $this->mailer->addReplyTo($address, $name); |
||||
| 427 | return $this; |
||||
| 428 | } |
||||
| 429 | |||||
| 430 | /** |
||||
| 431 | * Add an attachment from a path on the filesystem. |
||||
| 432 | * |
||||
| 433 | * @param string $path Path to the attachment |
||||
| 434 | * @param string $name Overrides the attachment name |
||||
| 435 | * |
||||
| 436 | * @return $this mailer object itself |
||||
| 437 | */ |
||||
| 438 | public function attachment($path, $name = '') |
||||
| 439 | { |
||||
| 440 | $this->params['attachment'][$path] = $name; |
||||
| 441 | $this->mailer->addAttachment($path, $name); |
||||
| 442 | return $this; |
||||
| 443 | } |
||||
| 444 | |||||
| 445 | /** |
||||
| 446 | * Create a message and send it. |
||||
| 447 | * |
||||
| 448 | * @return bool |
||||
| 449 | */ |
||||
| 450 | public function send(): bool |
||||
| 451 | { |
||||
| 452 | $eventHandler = new EventHandler(); |
||||
| 453 | $eventHandler->setParams(['mailer' => $this]); |
||||
| 454 | $eventHandler->trigger('MailerBeforeSend'); |
||||
| 455 | $toAddresses = $this->mailer->From . ' >> ' . \print_r($this->mailer->getToAddresses(), true); |
||||
|
0 ignored issues
–
show
Are you sure
print_r($this->mailer->getToAddresses(), true) of type string|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 456 | \App\Log::beginProfile("Mailer::send|{$toAddresses}", 'Mail|SMTP'); |
||||
| 457 | if ($this->mailer->send()) { |
||||
| 458 | \App\Log::endProfile("Mailer::send|{$toAddresses}", 'Mail|SMTP'); |
||||
| 459 | if (!empty($this->smtp['save_send_mail'])) { |
||||
| 460 | $this->saveMail(); |
||||
| 461 | } |
||||
| 462 | Log::trace('Mailer sent mail', 'Mailer'); |
||||
| 463 | $eventHandler->trigger('MailerAfterSend'); |
||||
| 464 | return true; |
||||
| 465 | } |
||||
| 466 | \App\Log::endProfile("Mailer::send|{$toAddresses}", 'Mail|SMTP'); |
||||
| 467 | Log::error('Mailer Error: ' . \print_r($this->mailer->ErrorInfo, true), 'Mailer'); |
||||
|
0 ignored issues
–
show
Are you sure
print_r($this->mailer->ErrorInfo, true) of type string|true can be used in concatenation?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 468 | if (!empty(static::$error)) { |
||||
| 469 | static::$error[] = '########################################'; |
||||
| 470 | } |
||||
| 471 | if (\is_array($this->mailer->ErrorInfo)) { |
||||
|
0 ignored issues
–
show
|
|||||
| 472 | foreach ($this->mailer->ErrorInfo as $error) { |
||||
| 473 | static::$error[] = $error; |
||||
| 474 | } |
||||
| 475 | } else { |
||||
| 476 | static::$error[] = $this->mailer->ErrorInfo; |
||||
| 477 | } |
||||
| 478 | $eventHandler->trigger('MailerAfterSendError'); |
||||
| 479 | return false; |
||||
| 480 | } |
||||
| 481 | |||||
| 482 | /** |
||||
| 483 | * Check connection. |
||||
| 484 | * |
||||
| 485 | * @return array |
||||
| 486 | */ |
||||
| 487 | public function test() |
||||
| 488 | { |
||||
| 489 | $this->mailer->SMTPDebug = 2; |
||||
| 490 | static::$error = []; |
||||
| 491 | $this->mailer->Debugoutput = function ($str, $level) { |
||||
| 492 | if (false !== strpos(strtolower($str), 'error') || false !== strpos(strtolower($str), 'failed')) { |
||||
| 493 | static::$error[] = trim($str); |
||||
| 494 | Log::error(trim($str), 'Mailer'); |
||||
| 495 | } else { |
||||
| 496 | Log::trace(trim($str), 'Mailer'); |
||||
| 497 | } |
||||
| 498 | }; |
||||
| 499 | $currentUser = \Users_Record_Model::getCurrentUserModel(); |
||||
| 500 | $this->to($currentUser->get('email1')); |
||||
| 501 | $templateId = Mail::getTemplateIdFromSysName('TestMailAboutTheMailServerConfiguration'); |
||||
| 502 | if (!$templateId) { |
||||
|
0 ignored issues
–
show
The expression
$templateId of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||||
| 503 | return ['result' => false, 'error' => Language::translate('LBL_NO_EMAIL_TEMPLATE')]; |
||||
| 504 | } |
||||
| 505 | $template = Mail::getTemplate($templateId); |
||||
| 506 | $textParser = TextParser::getInstanceById($currentUser->getId(), 'Users'); |
||||
| 507 | $this->subject($textParser->setContent($template['subject'])->parse()->getContent()); |
||||
| 508 | $this->content($textParser->setContent($template['content'])->parse()->getContent()); |
||||
|
0 ignored issues
–
show
$textParser->setContent(...->parse()->getContent() of type string is incompatible with the type App\text expected by parameter $message of App\Mailer::content().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 509 | return ['result' => $this->send(), 'error' => implode(PHP_EOL, static::$error)]; |
||||
| 510 | } |
||||
| 511 | |||||
| 512 | /** |
||||
| 513 | * Send mail by row queue. |
||||
| 514 | * |
||||
| 515 | * @param array $rowQueue |
||||
| 516 | * |
||||
| 517 | * @return bool |
||||
| 518 | */ |
||||
| 519 | public static function sendByRowQueue($rowQueue) |
||||
| 520 | { |
||||
| 521 | if ('demo' === \App\Config::main('systemMode')) { |
||||
| 522 | return true; |
||||
| 523 | } |
||||
| 524 | $mailer = (new self())->loadSmtpByID($rowQueue['smtp_id'])->subject($rowQueue['subject'])->content($rowQueue['content']); |
||||
| 525 | if ($rowQueue['from']) { |
||||
| 526 | $from = Json::decode($rowQueue['from']); |
||||
| 527 | $mailer->from($from['email'], $from['name']); |
||||
| 528 | } |
||||
| 529 | foreach (['cc', 'bcc'] as $key) { |
||||
| 530 | if ($rowQueue[$key]) { |
||||
| 531 | foreach (Json::decode($rowQueue[$key]) as $email => $name) { |
||||
| 532 | if (is_numeric($email)) { |
||||
| 533 | $email = $name; |
||||
| 534 | $name = ''; |
||||
| 535 | } |
||||
| 536 | $mailer->{$key}($email, $name); |
||||
| 537 | } |
||||
| 538 | } |
||||
| 539 | } |
||||
| 540 | $status = false; |
||||
| 541 | $attachmentsToRemove = $update = []; |
||||
| 542 | if ($rowQueue['attachments']) { |
||||
| 543 | $attachments = Json::decode($rowQueue['attachments']); |
||||
| 544 | if (isset($attachments['ids'])) { |
||||
| 545 | $attachments = array_merge($attachments, Mail::getAttachmentsFromDocument($attachments['ids'])); |
||||
| 546 | unset($attachments['ids']); |
||||
| 547 | } |
||||
| 548 | foreach ($attachments as $path => $name) { |
||||
| 549 | if (is_numeric($path)) { |
||||
| 550 | $path = $name; |
||||
| 551 | $name = ''; |
||||
| 552 | } |
||||
| 553 | $mailer->attachment($path, $name); |
||||
| 554 | if (strpos(realpath($path), 'cache' . \DIRECTORY_SEPARATOR)) { |
||||
| 555 | $attachmentsToRemove[] = $path; |
||||
| 556 | } |
||||
| 557 | } |
||||
| 558 | } |
||||
| 559 | if (!empty($rowQueue['params'])) { |
||||
| 560 | $mailer->setCustomParams(Json::decode($rowQueue['params'])); |
||||
| 561 | } |
||||
| 562 | if ($mailer->getSmtp('individual_delivery')) { |
||||
| 563 | $emails = []; |
||||
| 564 | foreach (Json::decode($rowQueue['to']) as $email => $name) { |
||||
| 565 | if (is_numeric($email)) { |
||||
| 566 | $email = $name; |
||||
| 567 | $name = ''; |
||||
| 568 | } |
||||
| 569 | $emails[$email] = $name; |
||||
| 570 | } |
||||
| 571 | foreach ($emails as $email => $name) { |
||||
| 572 | $separateMailer = $mailer->cloneMailer(); |
||||
| 573 | $separateMailer->to($email, $name); |
||||
| 574 | $status = $separateMailer->send(); |
||||
| 575 | if (!$status) { |
||||
| 576 | $update['to'] = Json::encode($emails); |
||||
| 577 | break; |
||||
| 578 | } |
||||
| 579 | unset($separateMailer, $emails[$email]); |
||||
| 580 | } |
||||
| 581 | } else { |
||||
| 582 | foreach (Json::decode($rowQueue['to']) as $email => $name) { |
||||
| 583 | if (is_numeric($email)) { |
||||
| 584 | $email = $name; |
||||
| 585 | $name = ''; |
||||
| 586 | } |
||||
| 587 | $mailer->to($email, $name); |
||||
| 588 | } |
||||
| 589 | $status = $mailer->send(); |
||||
| 590 | unset($mailer); |
||||
| 591 | } |
||||
| 592 | $db = Db::getInstance('admin'); |
||||
| 593 | if ($status) { |
||||
| 594 | $db->createCommand()->delete('s_#__mail_queue', ['id' => $rowQueue['id']])->execute(); |
||||
| 595 | foreach ($attachmentsToRemove as $file) { |
||||
| 596 | unlink($file); |
||||
| 597 | } |
||||
| 598 | } else { |
||||
| 599 | $update['status'] = 2; |
||||
| 600 | $update['error'] = implode(PHP_EOL, static::$error); |
||||
| 601 | $db->createCommand()->update('s_#__mail_queue', $update, ['id' => $rowQueue['id']])->execute(); |
||||
| 602 | } |
||||
| 603 | return $status; |
||||
| 604 | } |
||||
| 605 | |||||
| 606 | /** |
||||
| 607 | * Adding additional parameters. |
||||
| 608 | * |
||||
| 609 | * @param array $params |
||||
| 610 | * |
||||
| 611 | * @return void |
||||
| 612 | */ |
||||
| 613 | public function setCustomParams(array $params): void |
||||
| 614 | { |
||||
| 615 | $this->params['params'] = $params; |
||||
| 616 | if (isset($this->params['ics'])) { |
||||
| 617 | $this->mailer->Ical = $this->params['ics']; |
||||
| 618 | } |
||||
| 619 | } |
||||
| 620 | |||||
| 621 | /** |
||||
| 622 | * Get additional parameters. |
||||
| 623 | * |
||||
| 624 | * @return array |
||||
| 625 | */ |
||||
| 626 | public function getCustomParams(): array |
||||
| 627 | { |
||||
| 628 | return $this->params; |
||||
| 629 | } |
||||
| 630 | |||||
| 631 | /** |
||||
| 632 | * Save sent email. |
||||
| 633 | * |
||||
| 634 | * @throws \App\Exceptions\AppException |
||||
| 635 | * |
||||
| 636 | * @return bool |
||||
| 637 | */ |
||||
| 638 | public function saveMail() |
||||
| 639 | { |
||||
| 640 | if (empty($this->smtp['smtp_username']) && empty($this->smtp['smtp_password']) && empty($this->smtp['smtp_host'])) { |
||||
| 641 | Log::error('Mailer Error: No smtp data entered', 'Mailer'); |
||||
| 642 | return false; |
||||
| 643 | } |
||||
| 644 | $folder = Utils::convertCharacterEncoding($this->smtp['smtp_folder'], 'UTF-8', 'UTF7-IMAP'); |
||||
| 645 | $mbox = \OSSMail_Record_Model::imapConnect( |
||||
| 646 | $this->smtp['smtp_username'], |
||||
| 647 | Encryption::getInstance()->decrypt($this->smtp['smtp_password']), |
||||
| 648 | $this->smtp['smtp_host'] . ':' . $this->smtp['smtp_port'], $folder, false, |
||||
| 649 | [ |
||||
| 650 | 'validate_cert' => !empty($this->smtp['smtp_validate_cert']), |
||||
| 651 | 'imap_max_retries' => 0, |
||||
| 652 | 'imap_params' => [], |
||||
| 653 | 'imap_open_add_connection_type' => true, |
||||
| 654 | ] |
||||
| 655 | ); |
||||
| 656 | if (false === $mbox && !imap_last_error()) { |
||||
| 657 | static::$error[] = 'IMAP error - ' . imap_last_error(); |
||||
| 658 | Log::error('Mailer Error: IMAP error - ' . imap_last_error(), 'Mailer'); |
||||
| 659 | return false; |
||||
| 660 | } |
||||
| 661 | \App\Log::beginProfile(__METHOD__ . '|imap_append', 'Mail|IMAP'); |
||||
| 662 | imap_append($mbox, \OSSMail_Record_Model::$imapConnectMailbox, $this->mailer->getSentMIMEMessage(), '\\Seen'); |
||||
|
0 ignored issues
–
show
$mbox of type IMAP\Connection|false is incompatible with the type resource expected by parameter $imap_stream of imap_append().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 663 | \App\Log::endProfile(__METHOD__ . '|imap_append', 'Mail|IMAP'); |
||||
| 664 | |||||
| 665 | return true; |
||||
| 666 | } |
||||
| 667 | |||||
| 668 | /** |
||||
| 669 | * Clone the mailer object for individual shipment. |
||||
| 670 | * |
||||
| 671 | * @return \App\Mailer |
||||
| 672 | */ |
||||
| 673 | public function cloneMailer() |
||||
| 674 | { |
||||
| 675 | $clonedThis = clone $this; |
||||
| 676 | $clonedThis->mailer = clone $this->mailer; |
||||
| 677 | return $clonedThis; |
||||
| 678 | } |
||||
| 679 | } |
||||
| 680 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.