Passed
Push — developer ( 4e3135...f5c82a )
by Radosław
30:25 queued 12:59
created

Mailer::loadSmtp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1.008

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
ccs 4
cts 5
cp 0.8
cc 1
nc 1
nop 1
crap 1.008
1
<?php
2
3
/*
4
 * Mailer basic class.
5
 *
6
 * @package App
7
 *
8
 * @copyright YetiForce S.A.
9
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
10
 * @author    Mariusz Krzaczkowski <[email protected]>
11
 * @author    Radosław Skrzypczak <[email protected]>
12
 */
13
14
namespace App;
15
16
use PHPMailer\PHPMailer\PHPMailer;
17
18
class Mailer
19
{
20
	/** @var string[] Queue status */
21
	public static $statuses = [
22
		0 => 'LBL_PENDING_ACCEPTANCE',
23
		1 => 'LBL_WAITING_TO_BE_SENT',
24
		2 => 'LBL_ERROR_DURING_SENDING',
25
	];
26
27
	/** @var string[] Columns list that require JSON formatting */
28
	public static $quoteJsonColumn = ['from', 'to', 'cc', 'bcc', 'attachments', 'params'];
29
30
	/** @var string[] Columns list available in the database */
31
	public static $quoteColumn = ['smtp_id', 'date', 'owner', 'status', 'from', 'subject', 'content', 'to', 'cc', 'bcc', 'attachments', 'priority'];
32
33
	/** @var PHPMailer Instance */
34
	protected $mailer;
35
36
	/** @var array SMTP configuration */
37
	protected $smtp;
38
39
	/** @var array Default settings */
40
	private $default = [
41
		'mailer_type' => 'smtp',
42
		'password' => '',
43
		'default' => '',
44
		'name' => '',
45
		'host' => '',
46
		'port' => '',
47
		'username' => '',
48
		'authentication' => 1,
49
		'secure' => '',
50
		'options' => [],
51
		'from_email' => '',
52
		'from_name' => '',
53
		'reply_to' => '',
54
		'priority' => '',
55
		'confirm_reading_to' => '',
56
		'organization' => '',
57
		'unsubscribe' => '',
58
		'individual_delivery' => 1,
59
		'imap_username' => '',
60
		'imap_password' => '',
61
		'imap_validate_cert' => 0,
62
		'imap_host' => '',
63
		'imap_port' => 0,
64
		'imap_folder' => '',
65
		'save_send_mail' => 0,
66
		'authType' => '',
67
		'authProvider' => null,
68
		'mail_account' => 0
69
	];
70
71
	/** @var array Parameters for sending messages */
72
	protected $params = [];
73
74
	/** @var array Error logs */
75
	public static $error;
76
	/** @var bool Debug active */
77
	public $debug = false;
78
79
	/**
80
	 * Construct.
81
	 */
82
	public function __construct()
83
	{
84
		static::$error = [];
85
		$this->debug = \App\Config::debug('MAILER_DEBUG');
86
		$this->mailer = new PHPMailer(false);
87
		$this->mailer->XMailer = 'YetiForceCRM Mailer';
88 1
		$this->mailer->Hostname = 'YetiForceCRM';
89
		$this->mailer->FromName = 'YetiForce Mailer';
90 1
		$this->mailer->CharSet = \App\Config::main('default_charset');
91 1
	}
92
93
	/**
94
	 * Load configuration smtp by ID.
95 1
	 *
96 1
	 * @param int $smtpId Smtp ID
97 1
	 *
98 1
	 * @return $this mailer object itself
99
	 */
100
	public function loadSmtpByID(int $smtpId)
101
	{
102
		$this->loadSmtp(Mail::getSmtpById($smtpId));
103
		return $this;
104 1
	}
105 1
106
	/**
107
	 * Load configuration smtp.
108
	 *
109 1
	 * @param array $smtpInfo
110 1
	 *
111
	 * @return $this mailer object itself
112
	 */
113 1
	public function loadSmtp($smtpInfo)
114
	{
115
		$this->smtp = $this->parseData($smtpInfo);
116 1
		$this->setSmtp();
117 1
118 1
		return $this;
119 1
	}
120 1
121
	/**
122
	 * Parse data.
123 1
	 *
124
	 * @param array $smtpInfo
125
	 *
126 1
	 * @return array
127 1
	 */
128
	public function parseData(array $smtpInfo): array
129 1
	{
130
		$data = array_merge($this->default, array_intersect_key($smtpInfo, $this->default));
131
		if (('yfsmtp' === $data['mailer_type'])) {
132
			if (!($account = Mail\Account::getInstanceById((int) $data['mail_account'])) || !$account->isActive()) {
133
				static::$error[] = 'ERR_MAIL_ACCOUNT_NOT_ACTIVE';
134
				return [];
135
			}
136
			$data['mailer_type'] = 'smtp';
137 1
			$data['host'] = $account->getServer()->get('smtp_host');
138
			$data['port'] = $account->getServer()->get('smtp_port');
139 1
			$data['username'] = $account->getLogin();
140 1
			$data['authentication'] = 1;
141 1
			$data['secure'] = $account->getServer()->get('smtp_encrypt');
142 1
			$data['imap_username'] = $account->getLogin();
143 1
			$data['imap_validate_cert'] = $account->getServer()->get('imap_encrypt');
144
			$data['imap_host'] = $account->getServer()->get('imap_host');
145 1
			$data['imap_port'] = $account->getServer()->get('imap_port');
146 1
			if ($account->getServer()->isOAuth()) {
147
				$data['authType'] = 'XOAUTH2';
148 1
				$data['authProvider'] = new \PHPMailer\PHPMailer\OAuth(
149 1
					[
150 1
						'provider' => $account->getOAuthProvider()->getClient(),
151 1
						'clientId' => $account->getServer()->get('client_id'),
152 1
						'clientSecret' => $account->getServer()->getClientSecret(),
153 1
						'refreshToken' => $account->getRefreshToken(),
154
						'userName' => $account->getLogin(),
155
					]
156
				);
157
				if (!empty($data['save_send_mail'])) {
158
					$data['imap_password'] = $account->getPassword();
159
				}
160
			} else {
161
				$data['password'] = $data['imap_password'] = $account->getPassword();
162
			}
163
		} else {
164
			$data['password'] = empty($data['password']) ? '' : Encryption::getInstance()->decrypt($data['password']);
165
			$data['imap_password'] = empty($data['imap_password']) ? '' : Encryption::getInstance()->decrypt($data['imap_password']);
166
		}
167
		$options = $data['options'];
168
		if ($options && !\is_array($options)) {
169
			$data['options'] = Json::decode($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

169
			$data['options'] = Json::decode($options, /** @scrutinizer ignore-type */ true) ?: [];
Loading history...
170
		}
171
172
		return $data;
173
	}
174
175
	/**
176
	 * @param array $params
177
	 *
178
	 * @return bool
179
	 */
180
	public static function sendFromTemplate(array $params): bool
181 1
	{
182
		Log::trace('Send mail from template', 'Mailer');
183 1
		if (empty($params['template'])) {
184 1
			Log::warning('No template', 'Mailer');
185 1
			return false;
186
		}
187
		$recordModel = false;
188 1
		if (empty($params['recordModel'])) {
189
			$moduleName = $params['moduleName'] ?? null;
190
			if (isset($params['recordId'])) {
191 1
				$recordModel = \Vtiger_Record_Model::getInstanceById($params['recordId'], $moduleName);
192 1
			}
193
		} else {
194
			$recordModel = $params['recordModel'];
195
			unset($params['recordModel']);
196
		}
197
		$template = Mail::getTemplate($params['template']);
198
		if (!$template) {
199
			Log::warning('No mail template', 'Mailer');
200
			return false;
201
		}
202
		$textParser = $recordModel ? TextParser::getInstanceByModel($recordModel) : TextParser::getInstance($params['moduleName'] ?? '');
203
		if (!empty($params['language'])) {
204
			$textParser->setLanguage($params['language']);
205
		}
206
		if (!empty($params['sourceRecord'])) {
207
			$textParser->setSourceRecord($params['sourceRecord'], $params['sourceModule']);
208
		}
209
		$textParser->setParams(array_diff_key($params, array_flip(['subject', 'content', 'attachments', 'recordModel'])));
210
		$params['subject'] = $textParser->setContent($template['subject'])->parse()->getContent();
211
		$params['content'] = $textParser->setContent(\App\Utils\Completions::decode(\App\Purifier::purifyHtml($template['content'])))->parse()->getContent();
212
		unset($textParser);
213
		if (empty($params['smtp_id']) && !empty($template['smtp_id'])) {
214
			$params['smtp_id'] = $template['smtp_id'];
215
		}
216
		if (isset($template['attachments'])) {
217
			$params['attachments'] = array_merge(empty($params['attachments']) ? [] : $params['attachments'], $template['attachments']);
218
		}
219
		if (!empty($template['email_template_priority'])) {
220
			$params['priority'] = $template['email_template_priority'];
221
		}
222
		$row = array_intersect_key($params, array_flip(self::$quoteColumn));
223
		$row['params'] = array_diff_key($params, $row);
224
		return static::addMail($row);
225
	}
226
227
	/**
228
	 * Add mail to quote for send.
229
	 *
230
	 * @param array $params
231
	 *
232
	 * @return bool
233
	 */
234
	public static function addMail(array $params): bool
235
	{
236
		$response = false;
237
		$params['date'] = date('Y-m-d H:i:s');
238
		if (!\array_key_exists('status', $params)) {
239
			$params['status'] = Config::component('Mail', 'MAILER_REQUIRED_ACCEPTATION_BEFORE_SENDING') ? 0 : 1;
240
		}
241
		if (empty($params['owner'])) {
242
			$owner = User::getCurrentUserRealId();
243
			$params['owner'] = $owner ?: 0;
244
		}
245
		if (empty($params['smtp_id'])) {
246
			$params['smtp_id'] = Mail::getDefaultSmtp();
247
		}
248
		if (empty($params['smtp_id'])) {
249
			$params['error_code'] = 1;
250
			static::insertMailLog($params);
251
			Log::warning('No SMTP configuration', 'Mailer');
252
		} elseif (!\App\Mail::getSmtpById($params['smtp_id'])) {
253
			$params['error_code'] = 2;
254
			static::insertMailLog($params);
255
			Log::warning('SMTP configuration with provided id not exists', 'Mailer');
256
		} elseif (empty($params['to'])) {
257
			$params['error_code'] = 3;
258
			static::insertMailLog($params);
259
			Log::warning('No target email address provided', 'Mailer');
260
		} else {
261
			$smpt = \App\Mail::getSmtpById($params['smtp_id']);
262
			if ($smpt['individual_delivery']) {
263
				$to = $params['to'];
264
				if (!\is_array($to)) {
265
					$to = \App\Json::isJson($to) ? \App\Json::decode($to) : [$to];
266
				}
267
				foreach ($to as $key => $value) {
268
					$params['to'] = [$key => $value];
269
					$response = static::insertMail($params);
270
				}
271
			} else {
272
				$response = static::insertMail($params);
273
			}
274
		}
275
276
		return $response;
277
	}
278
279
	/**
280
	 * Add mail to queue.
281
	 *
282
	 * @param array $params
283
	 *
284
	 * @return bool
285
	 */
286
	public static function insertMail(array $params): bool
287
	{
288
		$eventHandler = new EventHandler();
289
		$eventHandler->setParams($params);
290
		$eventHandler->trigger('MailerAddToQueue');
291
		$params = $eventHandler->getParams();
292
293
		$fields = ['smtp_id', 'date', 'owner', 'status', 'from', 'subject', 'to', 'content', 'cc', 'bcc', 'attachments', 'params', 'priority', 'error'];
294
		$insertData = array_intersect_key($params, array_flip($fields));
295
		foreach (static::$quoteJsonColumn as $key) {
296
			if (isset($insertData[$key]) && (!\is_string($insertData[$key]) || !\App\Json::isJson($insertData[$key]))) {
297
				$insertData[$key] = Json::encode(!\is_array($insertData[$key]) ? [$insertData[$key]] : $insertData[$key]);
298
			}
299
		}
300
301
		return (bool) \App\Db::getInstance('admin')->createCommand()->insert('s_#__mail_queue', $insertData)->execute();
302
	}
303
304
	/**
305
	 * Save mail log data.
306
	 *
307
	 * @param array $params
308
	 *
309
	 * @return void
310
	 */
311
	public static function insertMailLog(array $params): void
312
	{
313
		$eventHandler = new EventHandler();
314
		$eventHandler->setParams($params);
315
		$eventHandler->trigger('MailerAddToLogs');
316
		$params = $eventHandler->getParams();
317
318
		$logFields = ['date', 'error_code', 'smtp_id', 'owner', 'status', 'from', 'subject', 'to', 'content', 'cc', 'bcc', 'attachments', 'params'];
319
		$insertData = array_intersect_key($params, array_flip($logFields));
320
		foreach (static::$quoteJsonColumn as $key) {
321
			if (isset($insertData[$key]) && (!\is_string($insertData[$key]) || !\App\Json::isJson($insertData[$key]))) {
322
				$insertData[$key] = Json::encode(!\is_array($insertData[$key]) ? [$insertData[$key]] : $insertData[$key]);
323
			}
324
		}
325
326
		\App\Db::getInstance('log')->createCommand()->insert('l_#__mail', $insertData)->execute();
327
	}
328
329
	/**
330
	 * Set configuration smtp in mailer.
331
	 */
332
	public function setSmtp(): void
333
	{
334
		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...
335
			static::$error[] = 'ERR_NO_SMTP_CONFIGURATION';
336
			return;
337
		}
338
		switch ($this->smtp['mailer_type']) {
339
			case 'smtp':
340
				$this->mailer->isSMTP();
341
				break;
342
			case 'sendmail':
343
				$this->mailer->isSendmail();
344
				break;
345
			case 'mail':
346
				$this->mailer->isMail();
347
				break;
348
			case 'qmail':
349
				$this->mailer->isQmail();
350
				break;
351
			default:
352
				break;
353
		}
354
355
		$this->mailer->Host = $this->smtp['host'];
356
		if (!empty($this->smtp['port'])) {
357
			$this->mailer->Port = $this->smtp['port'];
358
		}
359
		$this->mailer->SMTPSecure = $this->smtp['secure'];
360
		$this->mailer->SMTPAuth = (bool) $this->smtp['authentication'];
361
		$this->mailer->Username = trim($this->smtp['username']);
362
		if (!empty($this->smtp['password'])) {
363
			$this->mailer->Password = $this->smtp['password'];
364
		}
365
		if ($options = $this->smtp['options']) {
366
			$this->mailer->SMTPOptions = $options;
367
		}
368
		$this->mailer->setFrom($this->smtp['from_email'] ?: $this->smtp['username'], $this->smtp['from_name'] ?? '', false);
369
		if ($this->smtp['reply_to']) {
370
			$this->mailer->addReplyTo($this->smtp['reply_to']);
371
		}
372
		if ($this->smtp['unsubscribe']) {
373
			$unsubscribe = '';
374
			foreach (\App\Json::decode($this->smtp['unsubscribe']) as $row) {
375
				$unsubscribe .= "<$row>,";
376
			}
377
			$unsubscribe = rtrim($unsubscribe, ',');
378
			$this->mailer->AddCustomHeader('List-Unsubscribe', $unsubscribe);
379
		}
380
		if ($this->smtp['priority']) {
381
			$priorityName = $priority = $priorityX = null;
382
			switch ($this->smtp['priority']) {
383
				case 'normal':
384
				case 'Normal':
385
					$priorityX = 3;
386
					$priority = $priorityName = 'Normal';
387
					break;
388
				case 'non-urgent':
389
				case 'Low':
390
					$priorityX = 5;
391
					$priority = 'Non-Urgent';
392
					$priorityName = 'Low';
393
					break;
394
				case 'urgent':
395
				case 'High':
396
						$priorityX = 1;
397
						$priority = 'Urgent';
398
						$priorityName = 'High';
399
					break;
400
				default: break;
401
			}
402
			if ($priority) {
403
				$this->mailer->Priority = $priorityX;
404
				$this->mailer->AddCustomHeader('Priority', $priority);
405
				$this->mailer->AddCustomHeader('X-MSMail-Priority', $priorityName);
406
				$this->mailer->AddCustomHeader('Importance', $priorityName);
407
			}
408
		}
409
		if ($this->smtp['confirm_reading_to']) {
410
			$this->mailer->ConfirmReadingTo = $this->smtp['confirm_reading_to'];
411
		}
412
		if ($this->smtp['organization']) {
413
			$this->mailer->AddCustomHeader('Organization', $this->smtp['organization']);
414
		}
415
		if ($this->smtp['authType']) {
416
			$this->mailer->AuthType = $this->smtp['authType'];
417
		}
418
		if ($this->smtp['authProvider']) {
419
			$this->mailer->setOAuth($this->smtp['authProvider']);
420
		}
421
	}
422
423
	/**
424
	 * Set subject.
425
	 *
426
	 * @param string $subject
427
	 *
428
	 * @return $this mailer object itself
429
	 */
430
	public function subject($subject)
431
	{
432
		$this->params['subject'] = $this->mailer->Subject = $subject;
433
		return $this;
434
	}
435
436
	/**
437
	 * Creates a message from an HTML string, making modifications for inline images and backgrounds and creates a plain-text version by converting the HTML.
438
	 *
439
	 * @param text $message
440
	 *
441
	 * @see \PHPMailer::MsgHTML()
442
	 *
443
	 * @return $this mailer object itself
444
	 */
445
	public function content($message)
446
	{
447
		$this->params['body'] = $message;
448
		// Modification of the following condition will violate the license!
449
		if (!\App\YetiForce\Shop::check('YetiForceDisableBranding')) {
450
			$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>';
451
		}
452
		$this->mailer->isHTML(true);
453
		$this->mailer->msgHTML($message);
454
		return $this;
455
	}
456
457
	/**
458
	 * Set the From and FromName properties.
459
	 *
460
	 * @param string $address
461
	 * @param string $name
462
	 *
463
	 * @return $this mailer object itself
464
	 */
465
	public function from($address, $name = '')
466
	{
467
		$this->params['from'][$address] = $name;
468
		$this->mailer->setFrom($address, $name, false);
469
470
		return $this;
471
	}
472
473
	/**
474
	 * Add a "To" address.
475
	 *
476
	 * @param string $address The email address to send to
477
	 * @param string $name
478
	 *
479
	 * @return $this mailer object itself
480
	 */
481
	public function to($address, $name = '')
482
	{
483
		$this->params['to'][$address] = $name;
484
		$this->mailer->addAddress($address, $name);
485
		return $this;
486
	}
487
488
	/**
489
	 * Add a "CC" address.
490
	 *
491
	 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
492
	 *
493
	 * @param string $address The email address to send to
494
	 * @param string $name
495
	 *
496
	 * @return $this mailer object itself
497
	 */
498
	public function cc($address, $name = '')
499
	{
500
		$this->params['cc'][$address] = $name;
501
		$this->mailer->addCC($address, $name);
502
		return $this;
503
	}
504
505
	/**
506
	 * Add a "BCC" address.
507
	 *
508
	 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
509
	 *
510
	 * @param string $address The email address to send to
511
	 * @param string $name
512
	 *
513
	 * @return $this mailer object itself
514
	 */
515
	public function bcc($address, $name = '')
516
	{
517
		$this->params['bcc'][$address] = $name;
518
		$this->mailer->addBCC($address, $name);
519
		return $this;
520
	}
521
522
	/**
523
	 * Add a "Reply-To" address.
524
	 *
525
	 * @param string $address The email address to reply to
526
	 * @param string $name
527
	 *
528
	 * @return $this mailer object itself
529
	 */
530
	public function replyTo($address, $name = '')
531
	{
532
		$this->params['replyTo'][$address] = $name;
533
		$this->mailer->addReplyTo($address, $name);
534
		return $this;
535
	}
536
537
	/**
538
	 * Add an attachment from a path on the filesystem.
539
	 *
540
	 * @param string $path Path to the attachment
541
	 * @param string $name Overrides the attachment name
542
	 *
543
	 * @return $this mailer object itself
544
	 */
545
	public function attachment($path, $name = '')
546
	{
547
		$this->params['attachment'][$path] = $name;
548
		$this->mailer->addAttachment($path, $name);
549
		return $this;
550
	}
551
552
	/**
553
	 * Create a message and send it.
554
	 *
555
	 * @return bool
556
	 */
557
	public function send(): bool
558
	{
559
		if (static::$error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression static::error 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...
560
			return false;
561
		}
562
		if ($this->debug) {
563
			$this->mailer->SMTPDebug = \PHPMailer\PHPMailer\SMTP::DEBUG_SERVER;
564
			$this->mailer->Debugoutput = function ($str, $level) {
565
				if (false !== mb_stripos($str, 'error') || false !== mb_stripos($str, 'failed')) {
566
					static::$error[] = trim($str);
567
					Log::error(trim($str), 'Mailer');
568
				} else {
569
					Log::trace(trim($str), 'Mailer');
570
				}
571
			};
572
		}
573
		$eventHandler = new EventHandler();
574
		$eventHandler->setParams(['mailer' => $this]);
575
		$eventHandler->trigger('MailerBeforeSend');
576
577
		$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

577
		$toAddresses = $this->mailer->From . ' >> ' . /** @scrutinizer ignore-type */ \print_r($this->mailer->getToAddresses(), true);
Loading history...
578
		\App\Log::beginProfile("Mailer::send|{$toAddresses}", 'Mail|SMTP');
579
580
		if ($this->mailer->send()) {
581
			\App\Log::endProfile("Mailer::send|{$toAddresses}", 'Mail|SMTP');
582
			if (!empty($this->smtp['save_send_mail'])) {
583
				$this->saveMail();
584
			}
585
			Log::trace('Mailer sent mail', 'Mailer');
586
			$eventHandler->trigger('MailerAfterSend');
587
			return true;
588
		}
589
		\App\Log::endProfile("Mailer::send|{$toAddresses}", 'Mail|SMTP');
590
		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

590
		Log::error('Mailer Error: ' . /** @scrutinizer ignore-type */ \print_r($this->mailer->ErrorInfo, true), 'Mailer');
Loading history...
591
		if (!empty(static::$error)) {
592
			static::$error[] = '########################################';
593
		}
594
		if (\is_array($this->mailer->ErrorInfo)) {
0 ignored issues
show
introduced by
The condition is_array($this->mailer->ErrorInfo) is always false.
Loading history...
595
			foreach ($this->mailer->ErrorInfo as $error) {
596
				static::$error[] = $error;
597
			}
598
		} else {
599
			static::$error[] = $this->mailer->ErrorInfo;
600
		}
601
		$eventHandler->trigger('MailerAfterSendError');
602
		return false;
603
	}
604
605
	/**
606
	 * Send mail by row queue.
607
	 *
608
	 * @param array $rowQueue
609
	 *
610
	 * @return bool
611
	 */
612
	public static function sendByRowQueue($rowQueue)
613
	{
614
		if ('demo' === \App\Config::main('systemMode')) {
615
			return true;
616
		}
617
		$mailer = (new self())->loadSmtpByID($rowQueue['smtp_id'])->subject($rowQueue['subject'])->content($rowQueue['content']);
618
		if ($rowQueue['from']) {
619
			$from = Json::decode($rowQueue['from']);
620
			$mailer->from($from['email'], $from['name']);
621
		}
622
		foreach (['cc', 'bcc'] as $key) {
623
			if ($rowQueue[$key]) {
624
				foreach (Json::decode($rowQueue[$key]) as $email => $name) {
625
					if (is_numeric($email)) {
626
						$email = $name;
627
						$name = '';
628
					}
629
					$mailer->{$key}($email, $name);
630
				}
631
			}
632
		}
633
		$status = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $status is dead and can be removed.
Loading history...
634
		$attachmentsToRemove = $update = [];
635
		if ($rowQueue['attachments']) {
636
			$attachments = Json::decode($rowQueue['attachments']);
637
			if (isset($attachments['ids'])) {
638
				$attachments = array_merge($attachments, Mail::getAttachmentsFromDocument($attachments['ids']));
639
				unset($attachments['ids']);
640
			}
641
			foreach ($attachments as $path => $name) {
642
				if (is_numeric($path)) {
643
					$path = $name;
644
					$name = '';
645
				}
646
				$mailer->attachment($path, $name);
647
				if (strpos(realpath($path), 'cache' . \DIRECTORY_SEPARATOR)) {
648
					$attachmentsToRemove[] = $path;
649
				}
650
			}
651
		}
652
		if (!empty($rowQueue['params'])) {
653
			$mailer->setCustomParams(Json::decode($rowQueue['params']));
654
		}
655
		foreach (Json::decode($rowQueue['to']) as $email => $name) {
656
			if (is_numeric($email)) {
657
				$email = $name;
658
				$name = '';
659
			}
660
			$mailer->to($email, $name);
661
		}
662
		$status = $mailer->send();
663
		unset($mailer);
664
		$db = Db::getInstance('admin');
665
		if ($status) {
666
			$db->createCommand()->delete('s_#__mail_queue', ['id' => $rowQueue['id']])->execute();
667
			foreach ($attachmentsToRemove as $file) {
668
				unlink($file);
669
			}
670
		} else {
671
			$update['status'] = 2;
672
			$update['error'] = implode(PHP_EOL, static::$error);
673
			$db->createCommand()->update('s_#__mail_queue', $update, ['id' => $rowQueue['id']])->execute();
674
		}
675
		return $status;
676
	}
677
678
	/**
679
	 * Adding additional parameters.
680
	 *
681
	 * @param array $params
682
	 *
683
	 * @return void
684
	 */
685
	public function setCustomParams(array $params): void
686
	{
687
		$this->params['params'] = $params;
688
		if (isset($this->params['ics'])) {
689
			$this->mailer->Ical = $this->params['ics'];
690
		}
691
	}
692
693
	/**
694
	 * Get additional parameters.
695
	 *
696
	 * @return array
697
	 */
698
	public function getCustomParams(): array
699
	{
700
		return $this->params;
701
	}
702
703
	/**
704
	 * Save sent email.
705
	 *
706
	 * @throws \App\Exceptions\AppException
707
	 *
708
	 * @return bool
709
	 */
710
	public function saveMail()
711
	{
712
		$response = false;
713
		if (empty($this->smtp['imap_username']) || empty($this->smtp['imap_password']) || empty($this->smtp['imap_host']) || empty($this->smtp['imap_folder'])) {
714
			Log::error('Mailer Error: No imap data entered', 'Mailer');
715
			static::$error[] = 'Mailer Error: No imap data entered' . print_r([
0 ignored issues
show
Bug introduced by
Are you sure print_r(array($this->smt...['imap_folder']), 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

715
			static::$error[] = 'Mailer Error: No imap data entered' . /** @scrutinizer ignore-type */ print_r([
Loading history...
716
				$this->smtp['imap_username'],
717
				$this->smtp['imap_password'],
718
				$this->smtp['imap_host'],
719
				$this->smtp['imap_folder']
720
			], true);
721
			return $response;
722
		}
723
724
		$folderName = $this->smtp['imap_folder'];
725
		try {
726
			if ($this->smtp['mail_account']) {
727
				$imap = \App\Mail\Account::getInstanceById((int) $this->smtp['mail_account'])->openImap();
728
			} else {
729
				$imap = new \App\Mail\Connections\Imap([
730
					'host' => $this->smtp['imap_host'],
731
					'port' => $this->smtp['imap_port'],
732
					'encryption' => $this->smtp['imap_encrypt'],
733
					'validate_cert' => (bool) $this->smtp['imap_validate_cert'],
734
					'authentication' => null,
735
					'username' => $this->smtp['imap_username'],
736
					'password' => $this->smtp['imap_password']
737
				]);
738
				$imap->connect();
739
			}
740
741
			\App\Log::beginProfile(__METHOD__ . '|imap_append', 'Mail|IMAP');
742
			$response = $imap->appendMessage($folderName, $this->mailer->getSentMIMEMessage(), ['Seen']);
743
			\App\Log::endProfile(__METHOD__ . '|imap_append', 'Mail|IMAP');
744
		} catch (\Throwable $th) {
745
			static::$error[] = 'IMAP error - ' . $th->getMessage();
746
			Log::error('Mailer Error: IMAP error - ' . $th->getMessage(), 'Mailer');
747
		}
748
749
		return $response;
750
	}
751
}
752