Mailer::setSmtp()   F
last analyzed

Complexity

Conditions 24
Paths 19201

Size

Total Lines 80
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 600

Importance

Changes 0
Metric Value
eloc 66
dl 0
loc 80
ccs 0
cts 53
cp 0
rs 0
c 0
b 0
f 0
cc 24
nc 19201
nop 0
crap 600

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
Bug Best Practice introduced by
The expression $this->smtp of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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
Bug introduced by
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 ignore-type  annotation

263
			$this->mailer->SMTPOptions = Json::decode($this->smtp['options'], /** @scrutinizer ignore-type */ true);
Loading history...
Documentation Bug introduced by
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 $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

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
Bug introduced by
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 ignore-type  annotation

455
		$toAddresses = $this->mailer->From . ' >> ' . /** @scrutinizer ignore-type */ \print_r($this->mailer->getToAddresses(), true);
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
Bug introduced by
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 ignore-type  annotation

467
		Log::error('Mailer Error: ' . /** @scrutinizer ignore-type */ \print_r($this->mailer->ErrorInfo, true), 'Mailer');
Loading history...
468
		if (!empty(static::$error)) {
469
			static::$error[] = '########################################';
470
		}
471
		if (\is_array($this->mailer->ErrorInfo)) {
0 ignored issues
show
introduced by
The condition is_array($this->mailer->ErrorInfo) is always false.
Loading history...
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
Bug Best Practice introduced by
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 ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

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
Bug introduced by
$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 ignore-type  annotation

508
		$this->content(/** @scrutinizer ignore-type */ $textParser->setContent($template['content'])->parse()->getContent());
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
Bug introduced by
$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 ignore-type  annotation

662
		imap_append(/** @scrutinizer ignore-type */ $mbox, \OSSMail_Record_Model::$imapConnectMailbox, $this->mailer->getSentMIMEMessage(), '\\Seen');
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