encodingHelper = new EncodingHelper();
$this->editorHelper = new EditorHelper();
$this->userClass = new UserClass();
$this->mailClass = new MailClass();
$this->mailArchiveClass = new MailArchiveClass();
$this->config = acym_config();
$this->setFrom($this->getSendSettings('from_email'), $this->getSendSettings('from_name'));
$this->Sender = $this->cleanText($this->config->get('bounce_email'));
if (empty($this->Sender)) {
$this->Sender = '';
}
$maxLineLength = $this->config->get('mailer_wordwrap', 0);
if (!empty($maxLineLength) && self::MAX_LINE_LENGTH > $maxLineLength) {
$this->WordWrap = $maxLineLength;
}
$this->setSendingMethodSetting();
if ($this->config->get('dkim', 0) && $this->Mailer != 'elasticemail') {
$this->DKIM_domain = $this->config->get('dkim_domain');
$this->DKIM_selector = $this->config->get('dkim_selector', 'acy');
if (empty($this->DKIM_selector)) $this->DKIM_selector = 'acy';
$this->DKIM_passphrase = $this->config->get('dkim_passphrase');
$this->DKIM_identity = $this->config->get('dkim_identity');
$this->DKIM_private = trim($this->config->get('dkim_private'));
$this->DKIM_private_string = trim($this->config->get('dkim_private'));
}
$this->CharSet = strtolower($this->config->get('charset'));
if (empty($this->CharSet)) {
$this->CharSet = 'utf-8';
}
$this->clearAll();
$this->Encoding = $this->config->get('encoding_format');
if (empty($this->Encoding)) {
$this->Encoding = '8bit';
}
@ini_set('pcre.backtrack_limit', 1000000);
$this->SMTPOptions = ['ssl' => ['verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true]];
$this->addParamInfo();
}
private function getSendingMethodSettings($settingName, $default = ''): string
{
if (empty($this->currentMethodSetting)) {
return $this->config->get($settingName, $default);
}
return $this->currentMethodSetting[$settingName] ?? $default;
}
private function getSendingMethod(): string
{
$this->currentMethodSetting = [];
$sendingMethods = $this->config->get('sending_method_list', '{}');
$sendingMethods = json_decode($sendingMethods, true);
$sendingMethodsByList = $this->config->get('sending_method_list_by_list', '{}');
$sendingMethodsByList = json_decode($sendingMethodsByList, true);
if (empty($this->listsIds) || empty($sendingMethods) || empty($sendingMethodsByList) || empty($sendingMethodsByList[$this->listsIds[0]])) {
return $this->config->get('mailer_method', 'phpmail');
}
$sendingMethodIds = [];
foreach ($this->listsIds as $listId) {
if (!empty($sendingMethodsByList[$listId])) {
$sendingMethodIds[] = $sendingMethodsByList[$listId];
}
}
if (count($sendingMethodIds) !== count($this->listsIds) || count(array_unique($sendingMethodIds)) !== 1) {
return $this->config->get('mailer_method', 'phpmail');
}
$sendingMethodId = $sendingMethodIds[0];
if (empty($sendingMethods[$sendingMethodId])) {
return $this->config->get('mailer_method', 'phpmail');
}
$this->currentMethodSetting = $sendingMethods[$sendingMethodId];
return $sendingMethods[$sendingMethodId]['mailer_method'];
}
public function setSendingMethodSetting(): void
{
$externalSendingMethod = [];
acym_trigger('onAcymGetSendingMethods', [&$externalSendingMethod, true]);
$externalSendingMethod = array_keys($externalSendingMethod['sendingMethods']);
$mailerMethodConfig = $this->getSendingMethod();
$this->currentSendingMethod = $mailerMethodConfig;
if ($mailerMethodConfig === 'smtp') {
$this->isSMTP();
$this->Host = trim($this->getSendingMethodSettings('smtp_host'));
$port = $this->getSendingMethodSettings('smtp_port');
if (empty($port) && $this->getSendingMethodSettings('smtp_secured') === 'ssl') {
$port = 465;
}
if (!empty($port)) {
$this->Host .= ':'.$port;
}
$this->SMTPAuth = (bool)$this->getSendingMethodSettings('smtp_auth', true);
$this->Username = trim($this->getSendingMethodSettings('smtp_username'));
$connectionType = $this->getSendingMethodSettings('smtp_type');
$hostName = explode(':', $this->Host)[0];
if (OAuth::hostRequireOauth($hostName, $connectionType)) {
$this->AuthType = 'XOAUTH2';
$this->clientSecret = trim($this->getSendingMethodSettings('smtp_secret'));
$this->clientId = trim($this->getSendingMethodSettings('smtp_clientId'));
$this->refreshToken = trim($this->getSendingMethodSettings('smtp_refresh_token'));
$this->oauthToken = trim($this->getSendingMethodSettings('smtp_token'));
$this->expiredIn = trim($this->getSendingMethodSettings('smtp_token_expireIn'));
$oauth = new OAuth(
[
'userName' => $this->Username,
'clientSecret' => $this->clientSecret,
'oauthToken' => $this->oauthToken,
'clientId' => $this->clientId,
'refreshToken' => $this->refreshToken,
'expiredIn' => $this->expiredIn,
'host' => $hostName,
]
);
$this->setOAuth($oauth);
} else {
$authMethod = $this->getSendingMethodSettings('smtp_method');
if (in_array($authMethod, ['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'])) {
$this->AuthType = $authMethod;
}
if ($this->SMTPAuth) {
$this->Password = trim($this->getSendingMethodSettings('smtp_password'));
}
}
$this->SMTPSecure = trim((string)$this->getSendingMethodSettings('smtp_secured'));
if (empty($this->Sender)) {
$this->Sender = strpos($this->Username, '@') ? $this->Username : $this->getSendingMethodSettings('from_email');
}
} elseif ($mailerMethodConfig === 'sendmail') {
$this->isSendmail();
$this->Sendmail = trim($this->getSendingMethodSettings('sendmail_path'));
if (empty($this->Sendmail)) {
$this->Sendmail = '/usr/sbin/sendmail';
}
} elseif ($mailerMethodConfig === 'qmail') {
$this->isQmail();
} elseif ($mailerMethodConfig === 'amazon') {
$this->isSMTP();
$amazonCredentials = [];
acym_trigger('onAcymGetCredentialsSendingMethod', [&$amazonCredentials, 'amazon', $this->currentMethodSetting], 'plgAcymAmazon');
$this->Host = trim($amazonCredentials['amazon_host']).':587';
$this->Username = trim($amazonCredentials['amazon_username']);
$this->Password = trim($amazonCredentials['amazon_password']);
$this->SMTPAuth = true;
$this->SMTPSecure = 'tls';
} elseif ($mailerMethodConfig === 'brevo-smtp') {
$this->isSMTP();
$brevoSmtpCredentials = [];
acym_trigger('onAcymGetCredentialsSendingMethod', [&$brevoSmtpCredentials, 'brevo-smtp', $this->currentMethodSetting], 'plgAcymBrevo');
$this->Host = trim($brevoSmtpCredentials['brevo-smtp_host']).':587';
$this->Username = trim($brevoSmtpCredentials['brevo-smtp_username']);
$this->Password = trim($brevoSmtpCredentials['brevo-smtp_password']);
$this->SMTPAuth = true;
$this->SMTPSecure = 'tls';
} elseif (in_array($mailerMethodConfig, $externalSendingMethod)) {
$this->isExternal($mailerMethodConfig);
} else {
$this->isMail();
}
}
public function isExternal($method)
{
$this->Mailer = 'external';
$this->externalMailer = $method;
}
protected function externalSend($MIMEHeader, $MIMEBody)
{
$fromName = empty($this->FromName) ? $this->config->get('from_name') : $this->FromName;
$reply_to = array_shift($this->ReplyTo);
$attachments = [];
if (!empty($this->attachment) && $this->config->get('embed_files')) {
foreach ($this->attachment as $i => $oneAttach) {
$encodedContent = $this->encodeFile($oneAttach[0], $oneAttach[3]);
$this->attachment[$i]['contentEncoded'] = $encodedContent;
}
$attachments = $this->attachment;
}
$response = [];
$data = [
&$response,
$this,
['email' => $this->to[0][0], 'name' => $this->to[0][1]],
['email' => $this->From, 'name' => $fromName],
['email' => $reply_to[0], 'name' => $reply_to[1]],
!empty($this->bcc) ? $this->bcc : [],
$attachments,
$this->currentMethodSetting,
];
acym_trigger('onAcymSendEmail', $data);
if (!empty($response['error'])) {
$this->setError($response['message']);
return false;
}
return true;
}
public function send()
{
if (!file_exists(ACYM_LIBRARIES.'Mailer'.DS.'Mailer.php')) {
$this->reportMessage = acym_translationSprintf('ACYM_X_FILE_MISSING', 'Mailer', ACYM_LIBRARIES.'Mailer'.DS);
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
if (empty($this->Subject) || empty($this->Body)) {
if ($this->isTest && empty($this->Subject)) {
$this->Subject = acym_translation('ACYM_EMAIL_SUBJECT');
} else {
$this->reportMessage = acym_translation('ACYM_SEND_EMPTY');
$this->errorNumber = 8;
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
}
if (empty($this->ReplyTo) && empty($this->ReplyToQueue)) {
if (!empty($this->replyemail)) {
$replyToEmail = $this->replyemail;
} elseif ($this->config->get('from_as_replyto', 1) == 1) {
$replyToEmail = $this->getSendSettings('from_email');
} else {
$replyToEmail = $this->getSendSettings('replyto_email');
}
if (!empty($this->replyname)) {
$replyToName = $this->replyname;
} elseif ($this->config->get('from_as_replyto', 1) == 1) {
$replyToName = $this->getSendSettings('from_name');
} else {
$replyToName = $this->getSendSettings('replyto_name');
}
$this->_addReplyTo($replyToEmail, $replyToName);
}
$shouldEmbed = $this->config->get('embed_images', 0);
if (intval($shouldEmbed) === 1 && $this->Mailer !== 'elasticemail') {
$this->embedImages();
}
if (!$this->alreadyCheckedAddresses) {
$this->alreadyCheckedAddresses = true;
$replyToTmp = '';
if (!empty($this->ReplyTo)) {
$replyToTmp = reset($this->ReplyTo);
$replyToTmp = $replyToTmp[0];
} elseif (!empty($this->ReplyToQueue)) {
$replyToTmp = reset($this->ReplyToQueue);
$replyToTmp = $replyToTmp[1];
}
if (empty($replyToTmp) || !acym_isValidEmail($replyToTmp)) {
$this->reportMessage = acym_translation('ACYM_VALID_EMAIL').' ( '.acym_translation('ACYM_REPLYTO_EMAIL').' : '.(empty($this->ReplyTo) ? '' : $replyToTmp).' ) ';
$this->errorNumber = 9;
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
if (empty($this->From) || !acym_isValidEmail($this->From)) {
$this->reportMessage = acym_translation('ACYM_VALID_EMAIL').' ( '.acym_translation('ACYM_FROM_EMAIL').' : '.$this->From.' ) ';
$this->errorNumber = 9;
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
if (!empty($this->Sender) && !acym_isValidEmail($this->Sender)) {
$this->reportMessage = acym_translation('ACYM_VALID_EMAIL').' ( '.acym_translation('ACYM_BOUNCE_EMAIL').' : '.$this->Sender.' ) ';
$this->errorNumber = 9;
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
}
if ($this->config->get('save_body') === '1') {
@file_put_contents(ACYM_ROOT.'acydebug_mail.html', $this->Body);
}
$this->Body = htmlentities($this->Body);
$this->Body = htmlspecialchars_decode($this->Body);
$this->Body = str_replace(['&', 'ς'], ['&', 'ς'], $this->Body);
if ($this->CharSet !== 'utf-8') {
$this->Body = $this->encodingHelper->change($this->Body, 'UTF-8', $this->CharSet);
$this->Subject = $this->encodingHelper->change($this->Subject, 'UTF-8', $this->CharSet);
}
$this->Subject = str_replace(
['’', '“', '”', '–'],
["'", '"', '"', '-'],
$this->Subject
);
$this->Body = str_replace(" ", ' ', $this->Body);
$externalSending = false;
$this->isOneTimeMail = $this->isTransactional = $this->isForward || $this->isTest || $this->isSpamTest;
if (!empty($this->mailId) && !empty($this->defaultMail[$this->mailId])) {
if ($this->mailClass->isTransactionalMail($this->defaultMail[$this->mailId])) {
$this->isTransactional = true;
}
if ($this->mailClass->isOneTimeMail($this->defaultMail[$this->mailId])) {
$this->isOneTimeMail = true;
}
}
acym_trigger('onAcymProcessQueueExternalSendingCampaign', [&$externalSending, $this->isTransactional]);
$warnings = '';
if (ACYM_PRODUCTION) {
if ($externalSending) {
$result = false;
acym_trigger('onAcymRegisterReceiverContentAndList', [&$result, $this->Subject, $this->Body, $this->receiverEmail, $this->mailId, &$warnings]);
} else {
acym_trigger('onAcymBeforeEmailSend', [&$this]);
ob_start();
$result = parent::send();
$warnings = ob_get_clean();
}
}
if (!empty($warnings) && strpos($warnings, 'bloque')) {
$result = false;
}
$this->mailHeader = '';
$receivers = [];
foreach ($this->to as $oneReceiver) {
$receivers[] = $oneReceiver[0];
}
if (!$result) {
$this->reportMessage = acym_translationSprintf('ACYM_SEND_ERROR', ''.$this->Subject.'', ''.implode(' , ', $receivers).'');
if (!empty($this->ErrorInfo)) {
$this->reportMessage .= " \n\n ".$this->ErrorInfo;
}
if (!empty($warnings)) {
$this->reportMessage .= " \n\n ".$warnings;
}
$this->errorNumber = 1;
if ($this->report) {
$this->reportMessage = str_replace(
'Could not instantiate mail function',
''.acym_translation('ACYM_COUND_NOT_INSTANCIATE_MAIL_FUCNTION').'',
$this->reportMessage
);
acym_enqueueMessage(nl2br($this->reportMessage), 'error');
}
} else {
if ($this->isSendingMethodByListActive) {
$this->reportMessage = acym_translationSprintf(
'ACYM_SEND_SUCCESS_WITH_SENDING_METHOD',
''.$this->Subject.'',
''.implode(' , ', $receivers).'',
''.$this->currentSendingMethod.''
);
} else {
$this->reportMessage = acym_translationSprintf('ACYM_SEND_SUCCESS', ''.$this->Subject.'', ''.implode(' , ', $receivers).'');
}
if (!empty($warnings)) {
$this->reportMessage .= " \n\n ".$warnings;
}
if ($this->report) {
if (acym_isAdmin()) {
acym_enqueueMessage(preg_replace('#(
){2}#', '
', nl2br($this->reportMessage)), 'info');
}
}
}
return $result;
}
public function clearAll()
{
$this->Subject = '';
$this->Body = '';
$this->AltBody = '';
$this->ClearAllRecipients();
$this->ClearAttachments();
$this->ClearCustomHeaders();
$this->ClearReplyTos();
$this->errorNumber = 0;
$this->MessageID = '';
$this->ErrorInfo = '';
$this->setFrom($this->getSendSettings('from_email'), $this->getSendSettings('from_name'));
}
public function load(int $mailId, $user = null)
{
if (isset($this->defaultMail[$mailId])) {
return $this->defaultMail[$mailId];
}
if (!empty($this->overrideEmailToSend)) {
$this->defaultMail[$mailId] = $this->overrideEmailToSend;
} else {
$this->defaultMail[$mailId] = $this->mailClass->getOneById($mailId, true);
}
global $acymLanguages;
if (!$this->isAbTest && empty($this->overrideEmailToSend) && acym_isMultilingual()) {
$defaultLanguage = $this->config->get('multilingual_default', ACYM_DEFAULT_LANGUAGE);
$mails = $this->mailClass->getMultilingualMails($mailId);
$this->userLanguage = $user != null && !empty($user->language) ? $user->language : $defaultLanguage;
if (!empty($mails)) {
$languages = array_keys($mails);
if (count($languages) == 1) {
$key = $languages[0];
} elseif (empty($mails[$this->userLanguage])) {
$key = $defaultLanguage;
} else {
$key = $this->userLanguage;
}
if (isset($mails[$key])) {
$this->defaultMail[$mailId] = $mails[$key];
}
} else {
unset($this->defaultMail[$mailId]);
return false;
}
$acymLanguages['userLanguage'] = $this->userLanguage;
$this->setFrom($this->getSendSettings('from_email'), $this->getSendSettings('from_name'));
}
if (empty($this->defaultMail[$mailId]->id)) {
unset($this->defaultMail[$mailId]);
return false;
}
if (!empty($this->defaultMail[$mailId]->attachments)) {
$this->defaultMail[$mailId]->attach = [];
$attachments = json_decode($this->defaultMail[$mailId]->attachments);
foreach ($attachments as $oneAttach) {
$attach = new \stdClass();
$attach->name = basename($oneAttach->filename);
$attach->filename = str_replace(['/', '\\'], DS, ACYM_ROOT).$oneAttach->filename;
$attach->url = ACYM_LIVE.$oneAttach->filename;
$this->defaultMail[$mailId]->attach[] = $attach;
}
}
$this->mailId = $mailId;
$this->id = $this->mailId;
$this->mail = clone $this->defaultMail[$mailId];
acym_trigger('replaceContent', [&$this->defaultMail[$mailId], true]);
$this->prepareEmailContent($mailId);
if (!$this->isTest) {
$this->storeArchiveVersion($mailId);
}
return $this->defaultMail[$mailId];
}
private function storeArchiveVersion($mailId)
{
$mailId = intval($mailId);
if (empty($mailId) || $this->defaultMail[$mailId]->type !== MailClass::TYPE_STANDARD) {
return;
}
$mailArchive = new \stdClass();
$alreadyExisting = $this->mailArchiveClass->getOneByMailId($mailId);
if (!empty($alreadyExisting)) {
$mailArchive->id = $alreadyExisting->id;
}
$mailArchive->mail_id = $mailId;
$mailArchive->date = time();
$mailArchive->body = $this->defaultMail[$mailId]->body;
$mailArchive->subject = $this->defaultMail[$mailId]->subject;
$mailArchive->settings = $this->defaultMail[$mailId]->settings;
$mailArchive->stylesheet = $this->defaultMail[$mailId]->stylesheet;
$mailArchive->attachments = $this->defaultMail[$mailId]->attachments;
$this->mailArchiveClass->save($mailArchive);
}
private function canTrack(int $mailId, object $user): bool
{
if ($this->isTest || empty($mailId) || empty($user) || !isset($user->tracking) || $user->tracking != 1) {
return false;
}
$mail = $this->mailClass->getOneById($mailId);
if (!empty($mail) && $mail->tracking != 1) {
return false;
}
$lists = $this->mailClass->getAllListsByMailIdAndUserId($mailId, $user->id);
foreach ($lists as $list) {
if ($list->tracking != 1) {
return false;
}
}
return true;
}
private function loadUser($user)
{
if (is_string($user) && strpos($user, '@')) {
$receiver = $this->userClass->getOneByEmail($user);
if (empty($receiver) && $this->autoAddUser && acym_isValidEmail($user)) {
$newUser = new \stdClass();
$newUser->email = $user;
$this->userClass->checkVisitor = false;
$this->userClass->sendConf = false;
acym_setVar('acy_source', 'When sending a test');
$this->userClass->triggers = $this->userCreationTriggers;
$userId = $this->userClass->save($newUser);
$receiver = $this->userClass->getOneById($userId);
}
} elseif (is_object($user)) {
$receiver = $user;
} else {
$receiver = $this->userClass->getOneById($user);
}
$this->userLanguage = empty($receiver->language) ? acym_getLanguageTag() : $receiver->language;
$this->receiverEmail = $receiver->email;
return $receiver;
}
public function sendOne($mailId, $user, array $options = []): bool
{
$this->clearAll();
$receiver = $this->loadUser($user);
$this->isTest = !empty($options['isTest']);
$this->dtextsFailed = false;
if (empty($receiver->email)) {
$this->errorNumber = 4;
$this->reportMessage = acym_translationSprintf('ACYM_SEND_ERROR_USER', ''.acym_escape($user).'');
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
$mailId = intval($mailId);
$receiver->id = intval($receiver->id);
if (!$this->load($mailId, $receiver)) {
$this->reportMessage = 'Can not load the email with ID n°'.$mailId;
$this->errorNumber = 2;
if ($this->report) {
acym_enqueueMessage($this->reportMessage, 'error');
}
return false;
}
$this->prepareContent($mailId, $options);
$this->prepareHeaders($mailId, $receiver->id, $options);
$this->prepareAddresses($mailId, $receiver);
$this->prepareBcc($mailId, $options);
$this->prepareAttachments($mailId, $options);
$this->replaceDynamicContent($mailId);
$this->prepareTracking($mailId, $receiver);
if (!$this->handleDtexts($receiver)) {
$this->dtextsFailed = true;
return false;
}
$status = (bool)$this->send();
$this->initStatistics($status, $receiver->id);
return $status;
}
public function triggerFollowUpAgain(string $mailId, string $userId): void
{
if ($this->defaultMail[$mailId]->type !== MailClass::TYPE_FOLLOWUP) {
return;
}
$followUpClass = new FollowupClass();
$followUp = $followUpClass->getOneByMailId(intval($mailId));
if (empty($followUp)) {
return;
}
if (empty($followUp->loop)) {
return;
}
$lastEmail = $followUpClass->getLastEmail(intval($followUp->id));
if (empty($lastEmail) || $lastEmail->id != $mailId) {
return;
}
$delay = 0;
if (!empty($followUp->loop_delay)) {
$delay = $followUp->loop_delay;
}
$mailToSkip = [];
if (!empty($followUp->loop_mail_skip)) {
$mailToSkip = json_decode($followUp->loop_mail_skip);
}
$followUpClass->triggerFollowUp(intval($followUp->id), intval($userId), $delay, $mailToSkip);
}
private function prepareContent(int $mailId, array $options): void
{
$this->isHTML();
if (!empty($this->defaultMail[$mailId]->stylesheet)) {
$this->stylesheet = $this->defaultMail[$mailId]->stylesheet;
}
$this->settings = empty($this->defaultMail[$mailId]->settings) ? [] : json_decode($this->defaultMail[$mailId]->settings, true);
$this->Subject = $this->defaultMail[$mailId]->subject;
$this->Body = $this->defaultMail[$mailId]->body;
if ($this->isTest && !empty($options['testNote'])) {
$this->Body = '