PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.0.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.0.1
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Helpers / MailerHelper.php

MailerHelper.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.0.1, at back/Helpers/MailerHelper.php

1,688 lines 59.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Helpers;
4
5
6 use AcyMailing\Classes\CampaignClass;
7 use AcyMailing\Classes\FollowupClass;
8 use AcyMailing\Classes\MailArchiveClass;
9 use AcyMailing\Classes\MailClass;
10 use AcyMailing\Classes\OverrideClass;
11 use AcyMailing\Classes\UrlClass;
12 use AcyMailing\Classes\UserClass;
13 use AcyMailerPhp\Exception;
14 use AcyMailerPhp\OAuth;
15 use AcyMailerPhp\Mailer;
16 use Pelago\Emogrifier\HtmlProcessor\HtmlPruner;
17
18 class MailerHelper extends Mailer
19 {
20 public $XMailer = ' ';
21
22 public $SMTPAutoTLS = false;
23
24 public $to = [];
25 public $cc = [];
26 public $bcc = [];
27 public $ReplyTo = [];
28 public $attachment = [];
29 public $CustomHeader = [];
30 public $preheader;
31 public $replyname;
32 public $replyemail;
33 public $body;
34 public $altbody;
35 public $subject;
36 public $from;
37 public $fromName;
38 public $replyto;
39 public $custom_view;
40
41 private $encodingHelper;
42 private $editorHelper;
43 private $userClass;
44 private $mailClass;
45 private $mailArchiveClass;
46 public $config;
47
48 public $report = true;
49 public $alreadyCheckedAddresses = false;
50 public $errorNumber = 0;
51 public $errorNewTry = [1, 6];
52 public $failedCounting = true;
53 public $autoAddUser = false;
54 public $userCreationTriggers = true;
55 public string $reportMessage = '';
56 public bool $dtextsFailed = false;
57
58 public $trackEmail = false;
59
60 public $externalMailer;
61
62 public $stylesheet = '';
63 public $settings;
64
65 public $parameters = [];
66
67 public $userLanguage = '';
68 public $receiverEmail;
69
70 public $overrideEmailToSend = '';
71 public bool $isTest = false;
72 public $isSpamTest = false;
73 public $isForward = false;
74
75 public $clientId = '';
76 public $clientSecret = '';
77 public $refreshToken = '';
78 public $oauthToken = '';
79 public $expiredIn = '';
80 public $mailId = null;
81 public $creator_id;
82 public $type;
83 public $links_language;
84 public $id = null;
85 public $mail = null;
86 public $defaultMail = [];
87
88 public $listsIds = [];
89 private $currentMethodSetting = [];
90
91 public $isAbTest = false;
92 public $isTransactional = false;
93 public $isOneTimeMail = false;
94 private $currentSendingMethod = '';
95 public $isSendingMethodByListActive = false;
96
97 public function __construct()
98 {
99 parent::__construct();
100
101 $this->encodingHelper = new EncodingHelper();
102 $this->editorHelper = new EditorHelper();
103 $this->userClass = new UserClass();
104 $this->mailClass = new MailClass();
105 $this->mailArchiveClass = new MailArchiveClass();
106 $this->config = acym_config();
107 $this->setFrom($this->getSendSettings('from_email'), $this->getSendSettings('from_name'));
108 $this->Sender = $this->cleanText($this->config->get('bounce_email'));
109 if (empty($this->Sender)) {
110 $this->Sender = '';
111 }
112 $maxLineLength = $this->config->get('mailer_wordwrap', 0);
113 if (!empty($maxLineLength) && self::MAX_LINE_LENGTH > $maxLineLength) {
114 $this->WordWrap = $maxLineLength;
115 }
116
117 $this->setSendingMethodSetting();
118
119 if ($this->config->get('dkim', 0) && $this->Mailer != 'elasticemail') {
120 $this->DKIM_domain = $this->config->get('dkim_domain');
121 $this->DKIM_selector = $this->config->get('dkim_selector', 'acy');
122 if (empty($this->DKIM_selector)) $this->DKIM_selector = 'acy';
123 $this->DKIM_passphrase = $this->config->get('dkim_passphrase');
124 $this->DKIM_identity = $this->config->get('dkim_identity');
125 $this->DKIM_private = trim($this->config->get('dkim_private'));
126 $this->DKIM_private_string = trim($this->config->get('dkim_private'));
127 }
128
129 $this->CharSet = strtolower($this->config->get('charset'));
130 if (empty($this->CharSet)) {
131 $this->CharSet = 'utf-8';
132 }
133
134 $this->clearAll();
135
136 $this->Encoding = $this->config->get('encoding_format');
137 if (empty($this->Encoding)) {
138 $this->Encoding = '8bit';
139 }
140
141 @ini_set('pcre.backtrack_limit', 1000000);
142
143 $this->SMTPOptions = ['ssl' => ['verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true]];
144
145 $this->addParamInfo();
146 }
147
148 private function getSendingMethodSettings($settingName, $default = ''): string
149 {
150 if (empty($this->currentMethodSetting)) {
151 return $this->config->get($settingName, $default);
152 }
153
154 return $this->currentMethodSetting[$settingName] ?? $default;
155 }
156
157 private function getSendingMethod(): string
158 {
159 $this->currentMethodSetting = [];
160
161 $sendingMethods = $this->config->get('sending_method_list', '{}');
162 $sendingMethods = json_decode($sendingMethods, true);
163 $sendingMethodsByList = $this->config->get('sending_method_list_by_list', '{}');
164 $sendingMethodsByList = json_decode($sendingMethodsByList, true);
165
166 if (empty($this->listsIds) || empty($sendingMethods) || empty($sendingMethodsByList) || empty($sendingMethodsByList[$this->listsIds[0]])) {
167 return $this->config->get('mailer_method', 'phpmail');
168 }
169
170 $sendingMethodIds = [];
171
172 foreach ($this->listsIds as $listId) {
173 if (!empty($sendingMethodsByList[$listId])) {
174 $sendingMethodIds[] = $sendingMethodsByList[$listId];
175 }
176 }
177
178 if (count($sendingMethodIds) !== count($this->listsIds) || count(array_unique($sendingMethodIds)) !== 1) {
179 return $this->config->get('mailer_method', 'phpmail');
180 }
181
182 $sendingMethodId = $sendingMethodIds[0];
183
184 if (empty($sendingMethods[$sendingMethodId])) {
185 return $this->config->get('mailer_method', 'phpmail');
186 }
187
188 $this->currentMethodSetting = $sendingMethods[$sendingMethodId];
189
190 return $sendingMethods[$sendingMethodId]['mailer_method'];
191 }
192
193 public function setSendingMethodSetting(): void
194 {
195 $externalSendingMethod = [];
196 acym_trigger('onAcymGetSendingMethods', [&$externalSendingMethod, true]);
197 $externalSendingMethod = array_keys($externalSendingMethod['sendingMethods']);
198
199 $mailerMethodConfig = $this->getSendingMethod();
200 $this->currentSendingMethod = $mailerMethodConfig;
201
202 if ($mailerMethodConfig === 'smtp') {
203 $this->isSMTP();
204 $this->Host = trim($this->getSendingMethodSettings('smtp_host'));
205 $port = $this->getSendingMethodSettings('smtp_port');
206 if (empty($port) && $this->getSendingMethodSettings('smtp_secured') === 'ssl') {
207 $port = 465;
208 }
209 if (!empty($port)) {
210 $this->Host .= ':'.$port;
211 }
212 $this->SMTPAuth = (bool)$this->getSendingMethodSettings('smtp_auth', true);
213 $this->Username = trim($this->getSendingMethodSettings('smtp_username'));
214 $connectionType = $this->getSendingMethodSettings('smtp_type');
215 $hostName = explode(':', $this->Host)[0];
216
217 if (OAuth::hostRequireOauth($hostName, $connectionType)) {
218 $this->AuthType = 'XOAUTH2';
219
220 $this->clientSecret = trim($this->getSendingMethodSettings('smtp_secret'));
221 $this->clientId = trim($this->getSendingMethodSettings('smtp_clientId'));
222 $this->refreshToken = trim($this->getSendingMethodSettings('smtp_refresh_token'));
223 $this->oauthToken = trim($this->getSendingMethodSettings('smtp_token'));
224 $this->expiredIn = trim($this->getSendingMethodSettings('smtp_token_expireIn'));
225
226 $oauth = new OAuth(
227 [
228 'userName' => $this->Username,
229 'clientSecret' => $this->clientSecret,
230 'oauthToken' => $this->oauthToken,
231 'clientId' => $this->clientId,
232 'refreshToken' => $this->refreshToken,
233 'expiredIn' => $this->expiredIn,
234 'host' => $hostName,
235 ]
236 );
237 $this->setOAuth($oauth);
238 } else {
239 $authMethod = $this->getSendingMethodSettings('smtp_method');
240 if (in_array($authMethod, ['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'])) {
241 $this->AuthType = $authMethod;
242 }
243 if ($this->SMTPAuth) {
244 $this->Password = trim($this->getSendingMethodSettings('smtp_password'));
245 }
246 }
247
248 $this->SMTPSecure = trim((string)$this->getSendingMethodSettings('smtp_secured'));
249
250 if (empty($this->Sender)) {
251 $this->Sender = strpos($this->Username, '@') ? $this->Username : $this->getSendingMethodSettings('from_email');
252 }
253 } elseif ($mailerMethodConfig === 'sendmail') {
254 $this->isSendmail();
255 $this->Sendmail = trim($this->getSendingMethodSettings('sendmail_path'));
256 if (empty($this->Sendmail)) {
257 $this->Sendmail = '/usr/sbin/sendmail';
258 }
259 } elseif ($mailerMethodConfig === 'qmail') {
260 $this->isQmail();
261 } elseif ($mailerMethodConfig === 'amazon') {
262 $this->isSMTP();
263 $amazonCredentials = [];
264 acym_trigger('onAcymGetCredentialsSendingMethod', [&$amazonCredentials, 'amazon', $this->currentMethodSetting], 'plgAcymAmazon');
265 $this->Host = trim($amazonCredentials['amazon_host']).':587';
266 $this->Username = trim($amazonCredentials['amazon_username']);
267 $this->Password = trim($amazonCredentials['amazon_password']);
268 $this->SMTPAuth = true;
269 $this->SMTPSecure = 'tls';
270 } elseif ($mailerMethodConfig === 'brevo-smtp') {
271 $this->isSMTP();
272 $brevoSmtpCredentials = [];
273 acym_trigger('onAcymGetCredentialsSendingMethod', [&$brevoSmtpCredentials, 'brevo-smtp', $this->currentMethodSetting], 'plgAcymBrevo');
274 $this->Host = trim($brevoSmtpCredentials['brevo-smtp_host']).':587';
275 $this->Username = trim($brevoSmtpCredentials['brevo-smtp_username']);
276 $this->Password = trim($brevoSmtpCredentials['brevo-smtp_password']);
277 $this->SMTPAuth = true;
278 $this->SMTPSecure = 'tls';
279 } elseif (in_array($mailerMethodConfig, $externalSendingMethod)) {
280 $this->isExternal($mailerMethodConfig);
281 } else {
282 $this->isMail();
283 }
284 }
285
286 public function isExternal($method)
287 {
288 $this->Mailer = 'external';
289 $this->externalMailer = $method;
290 }
291
292 protected function externalSend($MIMEHeader, $MIMEBody)
293 {
294 $fromName = empty($this->FromName) ? $this->config->get('from_name') : $this->FromName;
295 $reply_to = array_shift($this->ReplyTo);
296
297 $attachments = [];
298 if (!empty($this->attachment) && $this->config->get('embed_files')) {
299 foreach ($this->attachment as $i => $oneAttach) {
300 $encodedContent = $this->encodeFile($oneAttach[0], $oneAttach[3]);
301 $this->attachment[$i]['contentEncoded'] = $encodedContent;
302 }
303 $attachments = $this->attachment;
304 }
305
306 $response = [];
307 $data = [
308 &$response,
309 $this,
310 ['email' => $this->to[0][0], 'name' => $this->to[0][1]],
311 ['email' => $this->From, 'name' => $fromName],
312 ['email' => $reply_to[0], 'name' => $reply_to[1]],
313 !empty($this->bcc) ? $this->bcc : [],
314 $attachments,
315 $this->currentMethodSetting,
316 ];
317 acym_trigger('onAcymSendEmail', $data);
318
319 if (!empty($response['error'])) {
320 $this->setError($response['message']);
321
322 return false;
323 }
324
325 return true;
326 }
327
328 public function send()
329 {
330 if (!file_exists(ACYM_LIBRARIES.'Mailer'.DS.'Mailer.php')) {
331 $this->reportMessage = acym_translationSprintf('ACYM_X_FILE_MISSING', 'Mailer', ACYM_LIBRARIES.'Mailer'.DS);
332 if ($this->report) {
333 acym_enqueueMessage($this->reportMessage, 'error');
334 }
335
336 return false;
337 }
338
339 if (empty($this->Subject) || empty($this->Body)) {
340 if ($this->isTest && empty($this->Subject)) {
341 $this->Subject = acym_translation('ACYM_EMAIL_SUBJECT');
342 } else {
343 $this->reportMessage = acym_translation('ACYM_SEND_EMPTY');
344 $this->errorNumber = 8;
345 if ($this->report) {
346 acym_enqueueMessage($this->reportMessage, 'error');
347 }
348
349 return false;
350 }
351 }
352
353 if (empty($this->ReplyTo) && empty($this->ReplyToQueue)) {
354 if (!empty($this->replyemail)) {
355 $replyToEmail = $this->replyemail;
356 } elseif ($this->config->get('from_as_replyto', 1) == 1) {
357 $replyToEmail = $this->getSendSettings('from_email');
358 } else {
359 $replyToEmail = $this->getSendSettings('replyto_email');
360 }
361
362 if (!empty($this->replyname)) {
363 $replyToName = $this->replyname;
364 } elseif ($this->config->get('from_as_replyto', 1) == 1) {
365 $replyToName = $this->getSendSettings('from_name');
366 } else {
367 $replyToName = $this->getSendSettings('replyto_name');
368 }
369
370 $this->_addReplyTo($replyToEmail, $replyToName);
371 }
372
373 $shouldEmbed = $this->config->get('embed_images', 0);
374 if (intval($shouldEmbed) === 1 && $this->Mailer !== 'elasticemail') {
375 $this->embedImages();
376 }
377
378 if (!$this->alreadyCheckedAddresses) {
379 $this->alreadyCheckedAddresses = true;
380
381 $replyToTmp = '';
382 if (!empty($this->ReplyTo)) {
383 $replyToTmp = reset($this->ReplyTo);
384 $replyToTmp = $replyToTmp[0];
385 } elseif (!empty($this->ReplyToQueue)) {
386 $replyToTmp = reset($this->ReplyToQueue);
387 $replyToTmp = $replyToTmp[1];
388 }
389
390 if (empty($replyToTmp) || !acym_isValidEmail($replyToTmp)) {
391 $this->reportMessage = acym_translation('ACYM_VALID_EMAIL').' ( '.acym_translation('ACYM_REPLYTO_EMAIL').' : '.(empty($this->ReplyTo) ? '' : $replyToTmp).' ) ';
392 $this->errorNumber = 9;
393 if ($this->report) {
394 acym_enqueueMessage($this->reportMessage, 'error');
395 }
396
397 return false;
398 }
399
400 if (empty($this->From) || !acym_isValidEmail($this->From)) {
401 $this->reportMessage = acym_translation('ACYM_VALID_EMAIL').' ( '.acym_translation('ACYM_FROM_EMAIL').' : '.$this->From.' ) ';
402 $this->errorNumber = 9;
403 if ($this->report) {
404 acym_enqueueMessage($this->reportMessage, 'error');
405 }
406
407 return false;
408 }
409
410 if (!empty($this->Sender) && !acym_isValidEmail($this->Sender)) {
411 $this->reportMessage = acym_translation('ACYM_VALID_EMAIL').' ( '.acym_translation('ACYM_BOUNCE_EMAIL').' : '.$this->Sender.' ) ';
412 $this->errorNumber = 9;
413 if ($this->report) {
414 acym_enqueueMessage($this->reportMessage, 'error');
415 }
416
417 return false;
418 }
419 }
420
421 if ($this->config->get('save_body') === '1') {
422 @file_put_contents(ACYM_ROOT.'acydebug_mail.html', $this->Body);
423 }
424
425 $this->Body = htmlentities($this->Body);
426 $this->Body = htmlspecialchars_decode($this->Body);
427 $this->Body = str_replace(['&amp;', '&sigmaf;'], ['&', 'ς'], $this->Body);
428
429 if ($this->CharSet !== 'utf-8') {
430 $this->Body = $this->encodingHelper->change($this->Body, 'UTF-8', $this->CharSet);
431 $this->Subject = $this->encodingHelper->change($this->Subject, 'UTF-8', $this->CharSet);
432 }
433
434 $this->Subject = str_replace(
435 ['’', '“', '”', '–'],
436 ["'", '"', '"', '-'],
437 $this->Subject
438 );
439
440 $this->Body = str_replace(" ", ' ', $this->Body);
441
442 $externalSending = false;
443
444 $this->isOneTimeMail = $this->isTransactional = $this->isForward || $this->isTest || $this->isSpamTest;
445 if (!empty($this->mailId) && !empty($this->defaultMail[$this->mailId])) {
446 if ($this->mailClass->isTransactionalMail($this->defaultMail[$this->mailId])) {
447 $this->isTransactional = true;
448 }
449
450 if ($this->mailClass->isOneTimeMail($this->defaultMail[$this->mailId])) {
451 $this->isOneTimeMail = true;
452 }
453 }
454
455 acym_trigger('onAcymProcessQueueExternalSendingCampaign', [&$externalSending, $this->isTransactional]);
456
457 $warnings = '';
458
459 if (ACYM_PRODUCTION) {
460 if ($externalSending) {
461 $result = false;
462 acym_trigger('onAcymRegisterReceiverContentAndList', [&$result, $this->Subject, $this->Body, $this->receiverEmail, $this->mailId, &$warnings]);
463 } else {
464 acym_trigger('onAcymBeforeEmailSend', [&$this]);
465 ob_start();
466 $result = parent::send();
467 $warnings = ob_get_clean();
468 }
469 }
470
471
472 if (!empty($warnings) && strpos($warnings, 'bloque')) {
473 $result = false;
474 }
475
476 $this->mailHeader = '';
477
478 $receivers = [];
479 foreach ($this->to as $oneReceiver) {
480 $receivers[] = $oneReceiver[0];
481 }
482
483 if (!$result) {
484 $this->reportMessage = acym_translationSprintf('ACYM_SEND_ERROR', '<b>'.$this->Subject.'</b>', '<b>'.implode(' , ', $receivers).'</b>');
485 if (!empty($this->ErrorInfo)) {
486 $this->reportMessage .= " \n\n ".$this->ErrorInfo;
487 }
488 if (!empty($warnings)) {
489 $this->reportMessage .= " \n\n ".$warnings;
490 }
491 $this->errorNumber = 1;
492 if ($this->report) {
493 $this->reportMessage = str_replace(
494 'Could not instantiate mail function',
495 '<a target="_blank" href="'.ACYM_DOCUMENTATION.'faq/could-not-instantiate-mail-function">'.acym_translation('ACYM_COUND_NOT_INSTANCIATE_MAIL_FUCNTION').'</a>',
496 $this->reportMessage
497 );
498 acym_enqueueMessage(nl2br($this->reportMessage), 'error');
499 }
500 } else {
501 if ($this->isSendingMethodByListActive) {
502 $this->reportMessage = acym_translationSprintf(
503 'ACYM_SEND_SUCCESS_WITH_SENDING_METHOD',
504 '<b>'.$this->Subject.'</b>',
505 '<b>'.implode(' , ', $receivers).'</b>',
506 '<b>'.$this->currentSendingMethod.'</b>'
507 );
508 } else {
509 $this->reportMessage = acym_translationSprintf('ACYM_SEND_SUCCESS', '<b>'.$this->Subject.'</b>', '<b>'.implode(' , ', $receivers).'</b>');
510 }
511 if (!empty($warnings)) {
512 $this->reportMessage .= " \n\n ".$warnings;
513 }
514 if ($this->report) {
515 if (acym_isAdmin()) {
516 acym_enqueueMessage(preg_replace('#(<br( ?/)?>){2}#', '<br />', nl2br($this->reportMessage)), 'info');
517 }
518 }
519 }
520
521 return $result;
522 }
523
524 public function clearAll()
525 {
526 $this->Subject = '';
527 $this->Body = '';
528 $this->AltBody = '';
529 $this->ClearAllRecipients();
530 $this->ClearAttachments();
531 $this->ClearCustomHeaders();
532 $this->ClearReplyTos();
533 $this->errorNumber = 0;
534 $this->MessageID = '';
535 $this->ErrorInfo = '';
536 $this->setFrom($this->getSendSettings('from_email'), $this->getSendSettings('from_name'));
537 }
538
539 public function load(int $mailId, $user = null)
540 {
541 if (isset($this->defaultMail[$mailId])) {
542 return $this->defaultMail[$mailId];
543 }
544
545 if (!empty($this->overrideEmailToSend)) {
546 $this->defaultMail[$mailId] = $this->overrideEmailToSend;
547 } else {
548 $this->defaultMail[$mailId] = $this->mailClass->getOneById($mailId, true);
549 }
550
551 global $acymLanguages;
552 if (!$this->isAbTest && empty($this->overrideEmailToSend) && acym_isMultilingual()) {
553 $defaultLanguage = $this->config->get('multilingual_default', ACYM_DEFAULT_LANGUAGE);
554 $mails = $this->mailClass->getMultilingualMails($mailId);
555
556 $this->userLanguage = $user != null && !empty($user->language) ? $user->language : $defaultLanguage;
557
558 if (!empty($mails)) {
559 $languages = array_keys($mails);
560 if (count($languages) == 1) {
561 $key = $languages[0];
562 } elseif (empty($mails[$this->userLanguage])) {
563 $key = $defaultLanguage;
564 } else {
565 $key = $this->userLanguage;
566 }
567
568 if (isset($mails[$key])) {
569 $this->defaultMail[$mailId] = $mails[$key];
570 }
571 } else {
572 unset($this->defaultMail[$mailId]);
573
574 return false;
575 }
576
577 $acymLanguages['userLanguage'] = $this->userLanguage;
578 $this->setFrom($this->getSendSettings('from_email'), $this->getSendSettings('from_name'));
579 }
580
581 if (empty($this->defaultMail[$mailId]->id)) {
582 unset($this->defaultMail[$mailId]);
583
584 return false;
585 }
586
587 if (!empty($this->defaultMail[$mailId]->attachments)) {
588 $this->defaultMail[$mailId]->attach = [];
589
590 $attachments = json_decode($this->defaultMail[$mailId]->attachments);
591 foreach ($attachments as $oneAttach) {
592 $attach = new \stdClass();
593 $attach->name = basename($oneAttach->filename);
594 $attach->filename = str_replace(['/', '\\'], DS, ACYM_ROOT).$oneAttach->filename;
595 $attach->url = ACYM_LIVE.$oneAttach->filename;
596 $this->defaultMail[$mailId]->attach[] = $attach;
597 }
598 }
599
600
601 $this->mailId = $mailId;
602 $this->id = $this->mailId;
603 $this->mail = clone $this->defaultMail[$mailId];
604 acym_trigger('replaceContent', [&$this->defaultMail[$mailId], true]);
605
606 $this->prepareEmailContent($mailId);
607 if (!$this->isTest) {
608 $this->storeArchiveVersion($mailId);
609 }
610
611 return $this->defaultMail[$mailId];
612 }
613
614 private function storeArchiveVersion($mailId)
615 {
616 $mailId = intval($mailId);
617 if (empty($mailId) || $this->defaultMail[$mailId]->type !== MailClass::TYPE_STANDARD) {
618 return;
619 }
620
621 $mailArchive = new \stdClass();
622
623 $alreadyExisting = $this->mailArchiveClass->getOneByMailId($mailId);
624 if (!empty($alreadyExisting)) {
625 $mailArchive->id = $alreadyExisting->id;
626 }
627
628 $mailArchive->mail_id = $mailId;
629 $mailArchive->date = time();
630 $mailArchive->body = $this->defaultMail[$mailId]->body;
631 $mailArchive->subject = $this->defaultMail[$mailId]->subject;
632 $mailArchive->settings = $this->defaultMail[$mailId]->settings;
633 $mailArchive->stylesheet = $this->defaultMail[$mailId]->stylesheet;
634 $mailArchive->attachments = $this->defaultMail[$mailId]->attachments;
635 $this->mailArchiveClass->save($mailArchive);
636 }
637
638 private function canTrack(int $mailId, object $user): bool
639 {
640 if ($this->isTest || empty($mailId) || empty($user) || !isset($user->tracking) || $user->tracking != 1) {
641 return false;
642 }
643
644 $mail = $this->mailClass->getOneById($mailId);
645 if (!empty($mail) && $mail->tracking != 1) {
646 return false;
647 }
648
649 $lists = $this->mailClass->getAllListsByMailIdAndUserId($mailId, $user->id);
650
651 foreach ($lists as $list) {
652 if ($list->tracking != 1) {
653 return false;
654 }
655 }
656
657 return true;
658 }
659
660 private function loadUser($user)
661 {
662 if (is_string($user) && strpos($user, '@')) {
663 $receiver = $this->userClass->getOneByEmail($user);
664
665 if (empty($receiver) && $this->autoAddUser && acym_isValidEmail($user)) {
666 $newUser = new \stdClass();
667 $newUser->email = $user;
668 $this->userClass->checkVisitor = false;
669 $this->userClass->sendConf = false;
670 acym_setVar('acy_source', 'When sending a test');
671 $this->userClass->triggers = $this->userCreationTriggers;
672 $userId = $this->userClass->save($newUser);
673 $receiver = $this->userClass->getOneById($userId);
674 }
675 } elseif (is_object($user)) {
676 $receiver = $user;
677 } else {
678 $receiver = $this->userClass->getOneById($user);
679 }
680
681 $this->userLanguage = empty($receiver->language) ? acym_getLanguageTag() : $receiver->language;
682 $this->receiverEmail = $receiver->email;
683
684 return $receiver;
685 }
686
687 public function sendOne($mailId, $user, array $options = []): bool
688 {
689 $this->clearAll();
690
691 $receiver = $this->loadUser($user);
692 $this->isTest = !empty($options['isTest']);
693 $this->dtextsFailed = false;
694
695 if (empty($receiver->email)) {
696 $this->errorNumber = 4;
697 $this->reportMessage = acym_translationSprintf('ACYM_SEND_ERROR_USER', '<b><i>'.acym_escape($user).'</i></b>');
698 if ($this->report) {
699 acym_enqueueMessage($this->reportMessage, 'error');
700 }
701
702 return false;
703 }
704
705 $mailId = intval($mailId);
706 $receiver->id = intval($receiver->id);
707
708 if (!$this->load($mailId, $receiver)) {
709 $this->reportMessage = 'Can not load the email with ID n°'.$mailId;
710 $this->errorNumber = 2;
711
712 if ($this->report) {
713 acym_enqueueMessage($this->reportMessage, 'error');
714 }
715
716 return false;
717 }
718
719 $this->prepareContent($mailId, $options);
720 $this->prepareHeaders($mailId, $receiver->id, $options);
721 $this->prepareAddresses($mailId, $receiver);
722 $this->prepareBcc($mailId, $options);
723 $this->prepareAttachments($mailId, $options);
724 $this->replaceDynamicContent($mailId);
725 $this->prepareTracking($mailId, $receiver);
726
727 if (!$this->handleDtexts($receiver)) {
728 $this->dtextsFailed = true;
729
730 return false;
731 }
732 $status = (bool)$this->send();
733
734 $this->initStatistics($status, $receiver->id);
735
736 return $status;
737 }
738
739 public function triggerFollowUpAgain(string $mailId, string $userId): void
740 {
741 if ($this->defaultMail[$mailId]->type !== MailClass::TYPE_FOLLOWUP) {
742 return;
743 }
744
745 $followUpClass = new FollowupClass();
746 $followUp = $followUpClass->getOneByMailId(intval($mailId));
747
748 if (empty($followUp)) {
749 return;
750 }
751
752 if (empty($followUp->loop)) {
753 return;
754 }
755
756 $lastEmail = $followUpClass->getLastEmail(intval($followUp->id));
757 if (empty($lastEmail) || $lastEmail->id != $mailId) {
758 return;
759 }
760
761 $delay = 0;
762 if (!empty($followUp->loop_delay)) {
763 $delay = $followUp->loop_delay;
764 }
765
766 $mailToSkip = [];
767 if (!empty($followUp->loop_mail_skip)) {
768 $mailToSkip = json_decode($followUp->loop_mail_skip);
769 }
770
771 $followUpClass->triggerFollowUp(intval($followUp->id), intval($userId), $delay, $mailToSkip);
772 }
773
774 private function prepareContent(int $mailId, array $options): void
775 {
776 $this->isHTML();
777
778 if (!empty($this->defaultMail[$mailId]->stylesheet)) {
779 $this->stylesheet = $this->defaultMail[$mailId]->stylesheet;
780 }
781 $this->settings = empty($this->defaultMail[$mailId]->settings) ? [] : json_decode($this->defaultMail[$mailId]->settings, true);
782
783 $this->Subject = $this->defaultMail[$mailId]->subject;
784 $this->Body = $this->defaultMail[$mailId]->body;
785 if ($this->isTest && !empty($options['testNote'])) {
786 $this->Body = '<div style="text-align: center; padding: 25px; font-family: Poppins; font-size: 20px">'.$options['testNote'].'</div>'.$this->Body;
787 }
788
789 if (!empty($this->introtext)) {
790 $this->Body = $this->introtext.$this->Body;
791 }
792
793 if (!empty($this->defaultMail[$mailId]->preheader)) {
794 $preHeader = '<!--[if !mso 9]><!--><div style="visibility:hidden;mso-hide:all;font-size:0;color:transparent;height:0;line-height:0;max-height:0;max-width:0;opacity:0;overflow:hidden;">';
795 $preHeader .= $this->defaultMail[$mailId]->preheader.str_repeat('&nbsp;&zwnj;', 100);
796 $preHeader .= '</div><!--<![endif]-->';
797
798 preg_match('#(<(.*)<body(.*)>)#Uis', $this->Body, $matches);
799 if (empty($matches) || empty($matches[1])) {
800 $this->Body = $preHeader.$this->Body;
801 } else {
802 $this->Body = $matches[1].$preHeader.str_replace($matches[1], '', $this->Body);
803 }
804 }
805
806 if (ACYM_CMS === 'wordpress') {
807 ob_start();
808 $this->Body = do_shortcode($this->Body);
809 ob_end_clean();
810 }
811 }
812
813 private function prepareHeaders(int $mailId, int $receiverId, array $options): void
814 {
815 $subject = base64_encode(rand(0, 9999999)).'AC'.$receiverId.'Y'.$this->defaultMail[$mailId]->id.'BA'.base64_encode(time().rand(0, 99999));
816 $this->MessageID = '<'.preg_replace('|[^a-z0-9+_]|i', '', $subject).'@'.$this->serverHostname().'>';
817 $this->addCustomHeader('Feedback-ID', $this->defaultMail[$mailId]->id.':'.$receiverId.':'.$this->defaultMail[$mailId]->type.':'.base64_encode(ACYM_ROOT));
818
819 if (!empty($this->defaultMail[$mailId]->headers)) {
820 $this->mailHeader = $this->defaultMail[$mailId]->headers;
821 }
822
823 if (!empty($options['headers'])) {
824 foreach ($options['headers'] as $headerName => $headerValue) {
825 $this->addCustomHeader(trim($headerName), trim($headerValue));
826 }
827 }
828 }
829
830 private function prepareAddresses(int $mailId, object $receiver): void
831 {
832 $receiverName = '';
833 if ($this->config->get('add_names', true)) {
834 $receiverName = $this->cleanText($receiver->name);
835 if ($receiverName == $this->cleanText($receiver->email)) {
836 $receiverName = '';
837 }
838 }
839 $this->addAddress($this->cleanText($receiver->email), $receiverName);
840
841 $this->setFrom($this->getSendSettings('from_email', $mailId), $this->getSendSettings('from_name', $mailId));
842 $this->_addReplyTo($this->defaultMail[$mailId]->reply_to_email, $this->defaultMail[$mailId]->reply_to_name);
843
844 if (!empty($this->defaultMail[$mailId]->bounce_email)) {
845 $this->Sender = $this->cleanText($this->defaultMail[$mailId]->bounce_email);
846 } else {
847 $this->Sender = $this->cleanText($this->config->get('bounce_email'));
848 }
849 }
850
851 private function prepareBcc(int $mailId, array $options): void
852 {
853 $bccAddresses = [];
854
855 if (!empty($options['bcc'])) {
856 $bccAddresses = $options['bcc'];
857 }
858
859 if (!empty($this->defaultMail[$mailId]->bcc)) {
860 $emailBccAddresses = explode(';', trim(str_replace([',', ' '], ';', $this->defaultMail[$mailId]->bcc)));
861 $bccAddresses = array_unique(array_merge($bccAddresses, $emailBccAddresses));
862 }
863
864 if (!empty($bccAddresses)) {
865 $bccAddresses = array_filter($bccAddresses);
866 foreach ($bccAddresses as $oneBccAddress) {
867 $this->loadUser($oneBccAddress);
868 $this->addBCC($oneBccAddress);
869 }
870 }
871 }
872
873 private function prepareAttachments(int $mailId, array $options): void
874 {
875 $attachments = [];
876
877 if (!empty($this->defaultMail[$mailId]->attach)) {
878 $attachments = $this->defaultMail[$mailId]->attach;
879 }
880
881 if (!empty($options['attachments'])) {
882 $attachments = array_merge($attachments, $options['attachments']);
883 }
884
885 if (empty($attachments)) {
886 return;
887 }
888
889 if ($this->config->get('embed_files')) {
890 foreach ($attachments as $attachment) {
891 $this->addAttachment($attachment->filename, $attachment->name);
892 }
893 } else {
894 $attachStringHTML = '<fieldset><legend>'.acym_translation('ACYM_ATTACHMENTS').'</legend><table>';
895 foreach ($attachments as $attachment) {
896 $attachStringHTML .= '<tr><td><a href="'.$attachment->url.'" target="_blank">'.$attachment->name.'</a></td></tr>';
897 }
898 $attachStringHTML .= '</table></fieldset>';
899
900 if ($this->config->get('attachments_position', 'bottom') === 'top') {
901 $this->Body = $attachStringHTML.'<br />'.$this->Body;
902 } else {
903 $this->Body .= '<br />'.$attachStringHTML;
904 }
905 }
906 }
907
908 private function replaceDynamicContent(int $mailId): void
909 {
910 $this->replaceParams();
911
912 $this->body = &$this->Body;
913 $this->altbody = &$this->AltBody;
914 $this->subject = &$this->Subject;
915 $this->from = &$this->From;
916 $this->fromName = &$this->FromName;
917 $this->replyto = &$this->ReplyTo;
918 $this->replyname = $this->defaultMail[$mailId]->reply_to_name;
919 $this->replyemail = $this->defaultMail[$mailId]->reply_to_email;
920 $this->mailId = $this->defaultMail[$mailId]->id;
921 $this->id = $this->mailId;
922 $this->creator_id = $this->defaultMail[$mailId]->creator_id;
923 $this->type = $this->defaultMail[$mailId]->type;
924 $this->stylesheet = &$this->stylesheet;
925 $this->links_language = $this->defaultMail[$mailId]->links_language;
926 }
927
928 private function prepareTracking(int $mailId, object $receiver): void
929 {
930 if ($this->canTrack($mailId, $receiver)) {
931 $this->statPicture($this->mailId, $receiver->id);
932 $this->body = acym_absoluteURL($this->body);
933 $this->statClick($this->mailId, $receiver->id);
934 if (acym_isTrackingSalesActive()) {
935 $this->trackingSales($this->mailId, $receiver->id);
936 }
937 }
938 }
939
940 private function handleDtexts(object $receiver): bool
941 {
942 $this->replaceParams();
943
944 if (strpos($receiver->email, '@mt.acyba.com') !== false) {
945 $currentUser = $this->userClass->getOneByEmail(acym_currentUserEmail());
946 if (empty($currentUser)) {
947 $currentUser = $receiver;
948 }
949 $result = acym_trigger('replaceUserInformation', [&$this, &$currentUser, true]);
950 } else {
951 $result = acym_trigger('replaceUserInformation', [&$this, &$receiver, true]);
952 if (!empty($result)) {
953 foreach ($result as $oneResult) {
954 if (!empty($oneResult) && !$oneResult['send']) {
955 $this->reportMessage = $oneResult['message'];
956
957 return false;
958 }
959 }
960 }
961 }
962
963 global $acymLanguages;
964 if (!empty($acymLanguages['userLanguage'])) {
965 unset($acymLanguages['userLanguage']);
966 }
967
968 foreach ($result as $oneResult) {
969 if (!empty($oneResult['emogrifier'])) {
970 $this->prepareEmailContent();
971 break;
972 }
973 }
974
975 if ($this->config->get('multiple_part', false)) {
976 $this->altbody = $this->textVersion($this->Body);
977 }
978
979 $this->replaceParams();
980
981 return true;
982 }
983
984 private function initStatistics(bool $status, int $receiverId): void
985 {
986 if ($this->trackEmail) {
987 $statsAdd = [];
988 $statsAdd[$this->mailId][intval($status)][] = $receiverId;
989
990 $queueHelper = new QueueHelper();
991 $queueHelper->statsAdd($statsAdd);
992 $this->trackEmail = false;
993 }
994 }
995
996 private function trackingSales($mailId, $userId)
997 {
998 preg_match_all('#href[ ]*=[ ]*"(?!mailto:|\#|ymsgr:|callto:|file:|ftp:|webcal:|skype:|tel:)([^"]+)"#Ui', $this->body, $results);
999 if (empty($results)) return;
1000
1001 foreach ($results[1] as $key => $url) {
1002 $simplifiedUrl = str_replace(['https://', 'http://', 'www.'], '', $url);
1003 $simplifiedWebsite = str_replace(['https://', 'http://', 'www.'], '', ACYM_LIVE);
1004 if (strpos($simplifiedUrl, rtrim($simplifiedWebsite, '/')) === false || strpos($url, 'task=unsub')) continue;
1005
1006 $toAddUrl = (strpos($url, '?') === false ? '?' : '&').'linkReferal='.$mailId.'-'.$userId;
1007
1008 $posHash = strpos($url, '#');
1009 if ($posHash !== false) {
1010 $newURL = substr($url, 0, $posHash).$toAddUrl.substr($url, $posHash);
1011 } else {
1012 $newURL = $url.$toAddUrl;
1013 }
1014
1015 $this->body = preg_replace('#href="('.preg_quote($url, '#').')"#Uis', 'href="'.$newURL.'"', $this->body);
1016 }
1017 }
1018
1019 public function statPicture($mailId, $userId)
1020 {
1021 $pictureLink = acym_frontendLink('frontstats&task=openStats&id='.$mailId.'&userid='.$userId, true, false);
1022
1023 $widthsize = 50;
1024 $heightsize = 1;
1025 $width = empty($widthsize) ? '' : ' width="'.$widthsize.'" ';
1026 $height = empty($heightsize) ? '' : ' height="'.$heightsize.'" ';
1027
1028 $statPicture = '<img class="spict" alt="Statistics image" src="'.$pictureLink.'" border="0" '.$height.$width.'/>';
1029
1030 if (strpos($this->body, '</body>')) {
1031 $this->body = str_replace('</body>', $statPicture.'</body>', $this->body);
1032 } else {
1033 $this->body .= $statPicture;
1034 }
1035 }
1036
1037 public function statClick($mailId, $userid, $fromStat = false)
1038 {
1039 if (!$fromStat && !in_array($this->type, MailClass::TYPES_WITH_STATS)) {
1040 return;
1041 }
1042
1043 $urlClass = new UrlClass();
1044 $urls = [];
1045
1046 $trackingSystemExternalWebsite = $this->config->get('trackingsystemexternalwebsite', 1);
1047 $trackingSystem = $this->config->get('trackingsystem', 'acymailing');
1048 if (false === strpos($trackingSystem, 'acymailing') && false === strpos($trackingSystem, 'google')) {
1049 return;
1050 }
1051
1052 if (strpos($trackingSystem, 'google') !== false) {
1053 $mail = $this->mailClass->getOneById($mailId);
1054 $campaignClass = new CampaignClass();
1055 $campaign = $campaignClass->getOneCampaignByMailId($mailId);
1056
1057 $utmCampaign = acym_getAlias($mail->subject);
1058 }
1059
1060 preg_match_all('#<[^>]* href[ ]*=[ ]*"(?!mailto:|\#|ymsgr:|callto:|file:|ftp:|webcal:|skype:|tel:)([^"]+)"#Ui', $this->body, $results);
1061 if (empty($results)) {
1062 return;
1063 }
1064
1065 $countLinks = array_count_values($results[1]);
1066 if (array_product($countLinks) != 1) {
1067 $previousLinkHandled = '';
1068 foreach ($results[1] as $key => $url) {
1069 if ($countLinks[$url] === 1) {
1070 continue;
1071 }
1072
1073 $previousIsOutlook = false;
1074 if (strpos($results[0][$key], '<v:roundrect') === 0) {
1075 $previousLinkHandled = $results[0][$key];
1076 if ($countLinks[$url] === 2) {
1077 $countLinks[$url] = 1;
1078 continue;
1079 }
1080 } elseif (strpos($previousLinkHandled, '<v:roundrect') === 0) {
1081 $previousIsOutlook = true;
1082 }
1083 $previousLinkHandled = $results[0][$key];
1084
1085 if (!$previousIsOutlook) {
1086 $countLinks[$url]--;
1087 }
1088
1089 $toAddUrl = (strpos($url, '?') === false ? '?' : '&').'idU='.$countLinks[$url];
1090
1091 if ($previousIsOutlook) {
1092 $countLinks[$url]--;
1093 }
1094
1095 $posHash = strpos($url, '#');
1096 if ($posHash !== false) {
1097 $newURL = substr($url, 0, $posHash).$toAddUrl.substr($url, $posHash);
1098 } else {
1099 $newURL = $url.$toAddUrl;
1100 }
1101
1102 $this->body = preg_replace('#href="('.preg_quote($url, '#').')"#Uis', 'href="'.$newURL.'"', $this->body, 1);
1103
1104 $results[0][$key] = 'href="'.$newURL.'"';
1105 $results[1][$key] = $newURL;
1106 }
1107 }
1108
1109 foreach ($results[1] as $i => $url) {
1110 $urlsNotToTrack = [
1111 'task=unsub',
1112 'fonts.googleapis.com',
1113 ];
1114
1115 $track = true;
1116
1117 foreach ($urlsNotToTrack as $urlNotToTrack) {
1118 if (strpos($url, $urlNotToTrack)) {
1119 $track = false;
1120 break;
1121 }
1122 }
1123
1124 if (isset($urls[$results[0][$i]]) || !$track) {
1125 continue;
1126 }
1127
1128 $simplifiedUrl = str_replace(['https://', 'http://', 'www.'], '', $url);
1129 $simplifiedWebsite = str_replace(['https://', 'http://', 'www.'], '', ACYM_LIVE);
1130 $internalUrl = strpos($simplifiedUrl, rtrim($simplifiedWebsite, '/')) === 0;
1131
1132 $subfolder = false;
1133 if ($internalUrl) {
1134 $urlWithoutBase = str_replace($simplifiedWebsite, '', $simplifiedUrl);
1135 if (strpos($urlWithoutBase, '/') || strpos($urlWithoutBase, '?')) {
1136 $slashPosition = strpos($urlWithoutBase, '/');
1137 $folderName = substr($urlWithoutBase, 0, !$slashPosition ? strpos($urlWithoutBase, '?') : $slashPosition);
1138 if (strpos($folderName, '.') === false) {
1139 $subfolder = @is_dir(ACYM_ROOT.$folderName);
1140 }
1141 }
1142 }
1143
1144 if ((!$internalUrl || $subfolder) && $trackingSystemExternalWebsite != 1) {
1145 continue;
1146 }
1147
1148 if (strpos($url, 'utm_source') === false && strpos($trackingSystem, 'google') !== false) {
1149 $idToUse = empty($campaign) ? $mailId : $campaign->id;
1150 $args = [];
1151 $args[] = empty($campaign->sending_params['utm_source']) ? 'utm_source=newsletter_'.$idToUse : 'utm_source='.urlencode($campaign->sending_params['utm_source']);
1152 $args[] = empty($campaign->sending_params['utm_medium']) ? 'utm_medium=email' : 'utm_medium='.urlencode($campaign->sending_params['utm_medium']);
1153 $args[] = empty($campaign->sending_params['utm_campaign']) ? 'utm_campaign='.$utmCampaign : 'utm_campaign='.urlencode($campaign->sending_params['utm_campaign']);
1154 $anchor = '';
1155 if (strpos($url, '#') !== false) {
1156 $anchor = substr($url, strpos($url, '#'));
1157 $url = substr($url, 0, strpos($url, '#'));
1158 }
1159
1160 if (strpos($url, '?')) {
1161 $mytracker = $url.'&'.implode('&', $args);
1162 } else {
1163 $mytracker = $url.'?'.implode('&', $args);
1164 }
1165 $mytracker .= $anchor;
1166 $urls[$results[0][$i]] = str_replace($results[1][$i], $mytracker, $results[0][$i]);
1167
1168 $url = $mytracker;
1169 }
1170
1171 if (strpos($trackingSystem, 'acymailing') !== false) {
1172 $isAutologin = false;
1173 $autologinParams = 'autoSubId=%7Bsubscriber:id%7D&amp;subKey=%7Bsubscriber:key%7Curlencode%7D';
1174 if (strpos($url, $autologinParams) !== false) {
1175 $isAutologin = true;
1176 $url = str_replace(
1177 [
1178 '?'.$autologinParams.'&amp;',
1179 '?'.$autologinParams,
1180 '&amp;'.$autologinParams,
1181 ],
1182 [
1183 '?',
1184 '',
1185 '',
1186 ],
1187 $url
1188 );
1189 }
1190
1191 if (preg_match('#passw|modify|\{|%7B#i', $url)) {
1192 continue;
1193 }
1194
1195 if (!$fromStat) {
1196 $mytracker = $urlClass->getUrl($url, $mailId, $userid);
1197 }
1198
1199 if (empty($mytracker)) {
1200 continue;
1201 }
1202
1203 if ($isAutologin) {
1204 $mytracker .= strpos($mytracker, '?') === false ? '?' : '&amp;';
1205 $mytracker .= $autologinParams;
1206 }
1207
1208 $urls[$results[0][$i]] = str_replace($results[1][$i], $mytracker, $results[0][$i]);
1209 }
1210 }
1211
1212 $this->body = str_replace(array_keys($urls), $urls, $this->body);
1213 }
1214
1215 public function textVersion($html, $fullConvert = true)
1216 {
1217 $html = acym_absoluteURL($html);
1218
1219 if ($fullConvert) {
1220 $html = preg_replace('# +#', ' ', $html);
1221 $html = str_replace(["\n", "\r", "\t"], '', $html);
1222 }
1223
1224 $removepictureslinks = "#< *a[^>]*> *< *img[^>]*> *< *\/ *a *>#isU";
1225 $removeScript = "#< *script(?:(?!< */ *script *>).)*< */ *script *>#isU";
1226 $removeStyle = "#< *style(?:(?!< */ *style *>).)*< */ *style *>#isU";
1227 $removeStrikeTags = '#< *strike(?:(?!< */ *strike *>).)*< */ *strike *>#iU';
1228 $replaceByTwoReturnChar = '#< *(h1|h2)[^>]*>#Ui';
1229 $replaceByStars = '#< *li[^>]*>#Ui';
1230 $replaceByReturnChar1 = '#< */ *(li|td|dt|tr|div|p)[^>]*> *< *(li|td|dt|tr|div|p)[^>]*>#Ui';
1231 $replaceByReturnChar = '#< */? *(br|p|h1|h2|legend|h3|li|ul|dd|dt|h4|h5|h6|tr|td|div)[^>]*>#Ui';
1232 $replaceLinks = '/< *a[^>]*href *= *"([^#][^"]*)"[^>]*>(.+)< *\/ *a *>/Uis';
1233
1234 $text = preg_replace(
1235 [
1236 $removepictureslinks,
1237 $removeScript,
1238 $removeStyle,
1239 $removeStrikeTags,
1240 $replaceByTwoReturnChar,
1241 $replaceByStars,
1242 $replaceByReturnChar1,
1243 $replaceByReturnChar,
1244 $replaceLinks,
1245 ],
1246 ['', '', '', '', "\n\n", "\n* ", "\n", "\n", '${2} ( ${1} )'],
1247 $html
1248 );
1249
1250 $text = preg_replace('#(&lt;|&\#60;)([^ \n\r\t])#i', '&lt; ${2}', $text);
1251
1252 $text = str_replace([" ", "&nbsp;"], ' ', strip_tags($text));
1253
1254 $text = trim(@html_entity_decode($text, ENT_QUOTES, 'UTF-8'));
1255
1256 if ($fullConvert) {
1257 $text = preg_replace('# +#', ' ', $text);
1258 $text = preg_replace('#\n *\n\s+#', "\n\n", $text);
1259 }
1260
1261 return $text;
1262 }
1263
1264 protected function embedImages()
1265 {
1266 preg_match_all('/(src|background)=[\'|"]([^"\']*)[\'|"]/Ui', $this->Body, $images);
1267
1268 if (empty($images[2])) {
1269 return true;
1270 }
1271
1272 $embedSuccess = true;
1273
1274 $mimetypes = [
1275 'bmp' => 'image/bmp',
1276 'gif' => 'image/gif',
1277 'jpeg' => 'image/jpeg',
1278 'jpg' => 'image/jpeg',
1279 'jpe' => 'image/jpeg',
1280 'png' => 'image/png',
1281 'tiff' => 'image/tiff',
1282 'tif' => 'image/tiff',
1283 ];
1284
1285 $allImages = [];
1286
1287 foreach ($images[2] as $i => $url) {
1288 if (isset($allImages[$url])) {
1289 continue;
1290 }
1291
1292 if (strpos($url, 'ctrl=') !== false) {
1293 continue;
1294 }
1295
1296 $allImages[$url] = 1;
1297
1298 $path = acym_internalUrlToPath($url);
1299 $path = $this->removeAdditionalParams($path);
1300
1301 $filename = str_replace(['%', ' '], '_', basename($url));
1302 $filename = $this->removeAdditionalParams($filename);
1303
1304 $md5 = md5($filename);
1305 $cid = 'cid:'.$md5;
1306 $fileParts = explode(".", $filename);
1307 if (empty($fileParts[1])) {
1308 continue;
1309 }
1310 $ext = strtolower($fileParts[1]);
1311 if (!isset($mimetypes[$ext])) {
1312 continue;
1313 }
1314
1315 if ($this->addEmbeddedImage($path, $md5, $filename, 'base64', $mimetypes[$ext])) {
1316 $this->Body = preg_replace('/'.preg_quote($images[0][$i], '/').'/Ui', $images[1][$i].'="'.$cid.'"', $this->Body);
1317 } else {
1318 $embedSuccess = false;
1319 }
1320 }
1321
1322 return $embedSuccess;
1323 }
1324
1325 private function removeAdditionalParams($url)
1326 {
1327 $additionalParamsPos = strpos($url, '?');
1328 if (!empty($additionalParamsPos)) {
1329 $url = substr($url, 0, $additionalParamsPos);
1330 }
1331
1332 return $url;
1333 }
1334
1335 public function cleanText($text)
1336 {
1337 return trim(preg_replace('/(%0A|%0D|\n+|\r+)/i', '', (string)$text));
1338 }
1339
1340 protected function _addReplyTo($email, $name)
1341 {
1342 if (empty($email)) {
1343 return;
1344 }
1345 $replyToName = $this->config->get('add_names', true) ? $this->cleanText(trim($name)) : '';
1346 $replyToEmail = trim($email);
1347 if (substr_count($replyToEmail, '@') > 1) {
1348 $replyToEmailArray = explode(';', str_replace([';', ','], ';', $replyToEmail));
1349 $replyToNameArray = explode(';', str_replace([';', ','], ';', $replyToName));
1350 foreach ($replyToEmailArray as $i => $oneReplyTo) {
1351 $this->addReplyTo($this->cleanText($oneReplyTo), @$replyToNameArray[$i]);
1352 }
1353 } else {
1354 $this->addReplyTo($this->cleanText($replyToEmail), $replyToName);
1355 }
1356 }
1357
1358 private function replaceParams()
1359 {
1360 if (empty($this->parameters)) return;
1361
1362 $helperPlugin = new PluginHelper();
1363
1364 $this->generateAllParams();
1365
1366 $vars = [
1367 'Subject',
1368 'Body',
1369 'From',
1370 'FromName',
1371 'replyname',
1372 'replyemail',
1373 ];
1374
1375 foreach ($vars as $oneVar) {
1376 if (!empty($this->$oneVar)) {
1377 $this->$oneVar = $helperPlugin->replaceDText($this->$oneVar, $this->parameters);
1378 }
1379 }
1380
1381 if (!empty($this->ReplyTo)) {
1382 foreach ($this->ReplyTo as $i => $replyto) {
1383 foreach ($replyto as $a => $oneval) {
1384 $this->ReplyTo[$i][$a] = $helperPlugin->replaceDText($this->ReplyTo[$i][$a], $this->parameters);
1385 }
1386 }
1387 }
1388 }
1389
1390 private function generateAllParams()
1391 {
1392 $result = '<table style="border:1px solid;border-collapse:collapse;" border="1" cellpadding="10"><tr><td>Tag</td><td>Value</td></tr>';
1393 foreach ($this->parameters as $name => $value) {
1394 if (!is_string($value)) continue;
1395
1396 $result .= '<tr><td>'.trim($name, '{}').'</td><td>'.$value.'</td></tr>';
1397 }
1398 $result .= '</table>';
1399 $this->addParam('allshortcodes', $result);
1400 }
1401
1402 public function addParamInfo()
1403 {
1404 if (!empty($_SERVER)) {
1405 $serverinfo = [];
1406 foreach ($_SERVER as $oneKey => $oneInfo) {
1407 $serverinfo[] = $oneKey.' => '.strip_tags(print_r($oneInfo, true));
1408 }
1409 $this->addParam('serverinfo', implode('<br />', $serverinfo));
1410 }
1411
1412 if (!empty($_REQUEST)) {
1413 $postinfo = [];
1414 foreach ($_REQUEST as $oneKey => $oneInfo) {
1415 $postinfo[] = $oneKey.' => '.strip_tags(print_r($oneInfo, true));
1416 }
1417 $this->addParam('postinfo', implode('<br />', $postinfo));
1418 }
1419 }
1420
1421 public function addParam($name, $value)
1422 {
1423 $tagName = '{'.$name.'}';
1424 $this->parameters[$tagName] = $value;
1425 }
1426
1427 public function overrideEmail(array $options): bool
1428 {
1429 $overrideAllEmails = $this->config->get('send_website_emails', 0) == 1;
1430
1431 $overrideClass = new OverrideClass();
1432 $override = $overrideClass->getMailByBaseContent($options['subject'], $options['message']);
1433
1434 if (empty($override) && !$overrideAllEmails) {
1435 return false;
1436 }
1437
1438 $this->autoAddUser = true;
1439
1440 if (empty($override)) {
1441 $this->trackEmail = false;
1442
1443 return $this->overrideAllEmails($options);
1444 } else {
1445 $this->trackEmail = true;
1446
1447 return $this->sendOverride($override, $options);
1448 }
1449 }
1450
1451 private function sendOverride(object $override, array $options): bool
1452 {
1453 for ($i = 1 ; $i < count($override->parameters) ; $i++) {
1454 $oneParam = $override->parameters[$i];
1455
1456 $unmodified = $oneParam;
1457 $oneParam = preg_replace(
1458 '/(http|https):\/\/(.*)/',
1459 '<a href="$1://$2" target="_blank">$1://$2</a>',
1460 $oneParam,
1461 -1,
1462 $count
1463 );
1464 if ($count > 0) {
1465 $this->addParam('link'.$i, $unmodified);
1466 }
1467 $this->addParam('param'.$i, $oneParam);
1468 }
1469
1470 $this->addParam('subject', $options['subject']);
1471
1472 $this->overrideEmailToSend = $override;
1473 $statusSend = $this->sendOne($override->id, $options['to'], $options);
1474 if (!$statusSend && !empty($this->reportMessage)) {
1475 $cronHelper = new CronHelper();
1476 $cronHelper->saveReport([$this->reportMessage]);
1477 }
1478
1479 return $statusSend;
1480 }
1481
1482 private function getSendSettings($type, $mailId = 0)
1483 {
1484 if (!in_array($type, ['from_name', 'from_email', 'replyto_name', 'replyto_email'])) return false;
1485
1486 $mailType = strpos($type, 'replyto') !== false ? str_replace('replyto', 'reply_to', $type) : $type;
1487
1488 if (!empty($mailId) && !empty($this->defaultMail[$mailId]) && !empty($this->defaultMail[$mailId]->$mailType)) return $this->defaultMail[$mailId]->$mailType;
1489
1490 $lang = empty($this->userLanguage) ? acym_getLanguageTag() : $this->userLanguage;
1491
1492 $setting = $this->config->get($type);
1493
1494 $translation = $this->config->get('sender_info_translation');
1495
1496 if (!empty($translation)) {
1497 $translation = json_decode($translation, true);
1498
1499 if (!empty($translation[$lang])) {
1500 $setting = $translation[$lang][$type];
1501 }
1502 }
1503
1504 return $setting;
1505 }
1506
1507 private function prepareEmailContent(?int $mailId = null): void
1508 {
1509 $this->handleRelativeURLs($mailId);
1510 $this->inlineCSS($mailId);
1511 $this->finalizeHtmlStructure($mailId);
1512 }
1513
1514 private function handleRelativeURLs(?int $mailId = null): void
1515 {
1516 $mail = empty($mailId) ? $this : $this->defaultMail[$mailId];
1517 $mail->body = acym_absoluteURL($mail->body);
1518 }
1519
1520 private function inlineCSS(?int $mailId = null): void
1521 {
1522 $mail = empty($mailId) ? $this : $this->defaultMail[$mailId];
1523
1524 global $emogrifiedMediaCSS;
1525 $emogrifiedMediaCSS = '';
1526
1527 $style = $this->getEmailStylesheet($mail);
1528 $cssInliner = CssInlinerHelper::fromHtml($mail->body)->inlineCss(implode('', $style));
1529 $domDocument = $cssInliner->getDomDocument();
1530
1531 $mail->body = preg_replace(
1532 [
1533 '# id="mce_\d+"#Ui',
1534 '#(style="[^"]+);"#Ui',
1535 ],
1536 [
1537 '',
1538 '$1"',
1539 ],
1540 HtmlPruner::fromDomDocument($domDocument)
1541 ->removeRedundantClassesAfterCssInlined($cssInliner)
1542 ->renderBodyContent()
1543 );
1544
1545 $mail->body = str_replace('&zwj;', '', $mail->body);
1546
1547 $mail->body = preg_replace_callback(
1548 '#style="([^"]+)"#Ui',
1549 function ($matches) {
1550 return 'style="'.str_replace(['; ', ': '], [';', ':'], $matches[1]).'"';
1551 },
1552 $mail->body
1553 );
1554 }
1555
1556 private function finalizeHtmlStructure(?int $mailId = null): void
1557 {
1558 $mail = empty($mailId) ? $this : $this->defaultMail[$mailId];
1559
1560 $finalContent = '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">';
1561 $finalContent .= '<head>';
1562 $finalContent .= '<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->';
1563 $finalContent .= '<meta http-equiv="Content-Type" content="text/html; charset='.strtolower($this->config->get('charset')).'" />'."\n";
1564 $finalContent .= '<meta name="viewport" content="width=device-width, initial-scale=1.0" />'."\n";
1565 $finalContent .= '<title>'.$mail->subject.'</title>'."\n";
1566
1567 global $emogrifiedMediaCSS;
1568 $finalContent .= '<style>'.$emogrifiedMediaCSS.'</style>';
1569 $finalContent .= '<!--[if mso]><style type="text/css">#acym__wysid__template center > table { width: 580px; }</style><![endif]-->';
1570 $finalContent .= '<!--[if !mso]><!--><style>#acym__wysid__template center > table { width: 100%; }</style><!--<![endif]-->';
1571
1572 if (!empty($mail->headers)) {
1573 $finalContent .= $mail->headers;
1574 }
1575 $finalContent .= '</head>';
1576
1577 preg_match('@<[^>"t]*body[^>]*>@', $mail->body, $matches);
1578 if (empty($matches[0])) {
1579 $mail->body = '<body>'.$mail->body.'</body>';
1580 } else {
1581 preg_match('@<[^>"t]*/body[^>]*>@', $mail->body, $matches);
1582 if (empty($matches[0])) {
1583 $mail->body .= '</body>';
1584 }
1585 }
1586
1587 $finalContent .= $mail->body;
1588 $finalContent .= '</html>';
1589
1590 $mail->body = $finalContent;
1591 }
1592
1593 private function getEmailStylesheet(object $mail): array
1594 {
1595 $style = [];
1596
1597 if (strpos($mail->body, 'acym__wysid__template') !== false) {
1598 static $foundationCSS = null;
1599 if (empty($foundationCSS)) {
1600 $foundationCSS = acym_fileGetContent(ACYM_MEDIA.'css'.DS.'libraries'.DS.'foundation_email.min.css');
1601 $foundationCSS = str_replace('#acym__wysid__template ', '', $foundationCSS);
1602 }
1603 $style['foundation'] = $foundationCSS;
1604 }
1605
1606 static $emailFixes = null;
1607 if (empty($emailFixes)) {
1608 $emailFixes = acym_getEmailCssFixes();
1609 }
1610 $style['fixes'] = $emailFixes;
1611
1612 if (!empty($mail->stylesheet)) {
1613 $style['custom_stylesheet'] = $mail->stylesheet;
1614 }
1615
1616 $settingsStyles = $this->editorHelper->getSettingsStyle($mail->settings);
1617 if (!empty($settingsStyles)) {
1618 $style['settings_stylesheet'] = $settingsStyles;
1619 }
1620
1621 return $style;
1622 }
1623
1624
1625 public function setFrom($email, $name = '', $auto = false)
1626 {
1627 if (!empty($email)) {
1628 $this->From = $this->cleanText($email);
1629 }
1630 if (!empty($name) && $this->config->get('add_names', true)) {
1631 $this->FromName = $this->cleanText($name);
1632 }
1633 }
1634
1635 protected function edebug($str)
1636 {
1637 if (strpos($this->ErrorInfo, $str) === false) {
1638 $this->ErrorInfo .= ' '.$str;
1639 }
1640 }
1641
1642 public function getMailMIME()
1643 {
1644 $result = parent::getMailMIME();
1645
1646 $result = rtrim($result, static::$LE);
1647
1648 if ($this->Mailer != 'mail') {
1649 $result .= static::$LE.static::$LE;
1650 }
1651
1652 return $result;
1653 }
1654
1655 public static function validateAddress($address, $patternselect = null)
1656 {
1657 return true;
1658 }
1659
1660 private function overrideAllEmails(array $options): bool
1661 {
1662 $this->trackEmail = false;
1663
1664 $this->addParam('subject', $options['subject']);
1665
1666 if (isset($options['isHtml']) && !$options['isHtml']) {
1667 $options['message'] = nl2br($options['message']);
1668 }
1669 $this->addParam('body', $options['message']);
1670
1671 $mailClass = new MailClass();
1672 $this->overrideEmailToSend = $mailClass->getOneByName('acy_notification_cms');
1673
1674 if (empty($this->overrideEmailToSend)) {
1675 return false;
1676 }
1677
1678 $statusSend = $this->sendOne($this->overrideEmailToSend->id, $options['to'], $options);
1679 if (!$statusSend && !empty($this->reportMessage)) {
1680 $cronHelper = new CronHelper();
1681 $cronHelper->addMessage($this->reportMessage);
1682 $cronHelper->saveReport();
1683 }
1684
1685 return $statusSend;
1686 }
1687 }
1688