9
Мар

PHP: mail() через внешние SMTP msmtp

Ситуация следующая. У вас есть сервер за Cloudflare и вы не хотите светить его ip в заголовках письма. Или у вас хостер закрыл 25й порт, ну или сервер стоит дома и провайдер закрыл 25й порт. Вариантов множество.

Есть решение. Но как всегда с подводными камнями в настройке и работе.

Для начала устанавливаем на Centos

yum install msmtp

Или Ubuntu

apt-get install msmtp

Говорят, нужно создать директорию

~/ named .msmtprc

Но я просто создал файл конфига на Ubuntu

nano /etc/msmtprc
defaults
tls on
tls_starttls on
tls_certcheck off
logfile /var/log/msmtp

account mail_yandex
host smtp.yandex.ru
port 25
auth on
user info@****.com.ua
password ************
from info@****.com.ua

account mail_server_2
host smtp.hd.zp.ua
port 25
auth on
user [email protected]
password *******
from [email protected]

account default : mail_server_2

Тут вы можете указать сразу несколько конфигов для подключения к почтовым серверам и использовать их как вам будет угодно в дальнейшем, о чём чуть дальше.
Обратите внимание, что по умолчанию мы указали 2й конфиг с названием mail_server_2

И так, сразу возможно возникнет проблема с тем, что msmtp не сможет писать лог.
Можно встретить примеры конфигов, где файл лога указывается как logfile /var/log/msmtp.txt
Но выдача прав на файл и вообще любые танцы с бубном ни к чему не привели. Поэтому используем без .txt

Если вдруг ничего не помогло:

sudo touch /var/log/msmtp
sudo chown -R root:mail /var/log/msmtp
sudo chmod -R 660 /var/log/msmtp
sudo chown root:msmtp /etc/msmtprc
sudo chmod 640 /etc/msmtprc

Дальше нужно установить симлинки

ln -s /usr/bin/msmtp /usr/sbin/sendmail
ln -s /usr/bin/msmtp /usr/bin/sendmail
ln -s /usr/bin/msmtp /usr/lib/sendmail

у меня ругалось что sendmail есть и пришлось его ликвидировать

sudo rm /usr/sbin/sendmail

После этого устанавливаем.

После этого необходимо подменить отправку в php.ini:

sendmail_path = "/usr/bin/msmtp -t"

Для виртуальныж хостов вызываем нужную нам секцию отправки:

php_admin_value sendmail_path "/usr/bin/msmtp -a mail_yandex -t"

Тут вы указываете какой конфиг использовать для отправки почты. Ведь у вас может быть много сайтов на сервере и много разных ящиков для отправки почты.

Для VestaCP или Hestia нужно указать почту в секции nano /home/пользователь/conf/web/хост/apache2.ssl.conf

    <Directory /home/caca/web/hd.zp.ua/public_html>
        AllowOverride All
        SSLRequireSSL
        Options +Includes -Indexes +ExecCGI
        php_admin_value sendmail_path "/usr/bin/msmtp -a mail_yandex -t"
    </Directory>

И перегружаем Apache:

sudo service apache2 restart

Вроде бы и всё, но нет.
Можно протестировать почту из консоли

echo "Test message." | msmtp -a mail_yandex [email protected]

Почта должна уйти.
Если уже прописали в виртуальные хосты, то также должна уходить, но есть два момента:
1. Почта не уходит. Возможно, php.ini для консоли и для виртуального хоста используется разный. Проверьте.
2. Почта с сайта уходит очень долго и поэтому всё жутко тупит до отправки. Ведь php скрипт ждёт true в ответ на отправку почты и всё это время сайт для вас висит.

Решение костыльное, но есть. Но увы, нужны минимальные знания php и вашего скрипта.
Нужно либо добавить в скрипт асинхронную отправку почты, либо вместо отправки из скрипта, сделать скрипт добавления писем в базу данных Mysql и раз в 1-5 минут по cron делать отправку.

Создаём таблицу MySQL

CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
to_email VARCHAR(255) NOT NULL,
from_email VARCHAR(255) NOT NULL,
subject VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
mail_headers TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Для примера, форум SMF
Файл Subs-Post.php

Вместо

if (!mail(strtr($to, array("\r" => '', "\n" => '')), $subject, $message, $headers, sprintf("-oi -f %s", $webmaster_email)))
{
	log_error(sprintf($txt['mail_send_unable'], $to));
	$mail_result = false;
}
$to = strtr($to, array("\r" => '', "\n" => ''));
$smcFunc['db_insert']('',
	'messages', // Название таблицы для логирования
	array(
		'subject' => 'string',
		'message' => 'string',
		'mail_headers' => 'string',
		'from_email' => 'string',
		'to_email' => 'string',
	),
	array(
		'subject' => $subject,
		'message' => $message,
		'mail_headers' => $headers,
		'from_email' => $webmaster_email,
		'to_email' => $to,
	),
	array('id')
);

для DLE
в файле mail.class.php
Вместо

if( !@mail( $this->to, $this->subject, $this->message, $this->mail_headers, $this->additional_parameters )  ) {
	if( !@mail( $this->to, $this->subject, $this->message, $this->mail_headers)  ) {
		$this->smtp_msg = "PHP Mail Error.";
		$this->send_error = true;
	}
}
$to = $db->safesql($to);
$from = $db->safesql($this->from);
$subject = $db->safesql($subject);
$message = $db->safesql($this->message);
$mail_headers = $db->safesql($this->mail_headers);
$query = "INSERT INTO messages (to_email, from_email, subject, message, mail_headers) VALUES ('$to', '$from', '$subject', '$message', '$mail_headers')";
$db->query($query);

В итоге у нас будут заносится наши письма с нужными заголовками в базу данных. Теперь нужно создать скрипт отправки по cron

send_mes.php

<?php
// Подключение к базе данных
$mysqli = new mysqli('localhost', 'пользователь', 'пароль', 'ваша_база_данных');

// Проверка соединения
if ($mysqli->connect_errno) {
    echo "Не удалось подключиться к MySQL: " . $mysqli->connect_error;
    exit();
}

// Получение сообщений из очереди
$query = "SELECT * FROM messages";
$result = $mysqli->query($query);

$idsToDelete = array(); // Массив для хранения идентификаторов записей, которые нужно удалить

if ($result->num_rows > 0) {
    // Отправка сообщений
    while ($row = $result->fetch_assoc()) {
        $to = $row['to_email'];
        $subject = $row['subject'];
        $message = $row['message'];
        $mail_headers = $row['mail_headers'];
		$id = $row['id'];

        // Отправка письма
        if (mail($to, $subject, $message, $mail_headers)) {
            // Добавляем идентификатор записи в массив для удаления
            $idsToDelete[] = $id;
			echo "Отправлено $to.";
        } else {
            echo "Ошибка при отправке сообщения на адрес $to.";
        }
    }
} else {
    echo "Нет сообщений в очереди.";
}

// Удаление записей после завершения цикла
if (!empty($idsToDelete)) {
    $idsString = implode(',', $idsToDelete);
    $deleteQuery = "DELETE FROM messages WHERE id IN ($idsString)";
    if ($mysqli->query($deleteQuery)) {
        echo "Удалено записей из базы данных: " . count($idsToDelete);
    } else {
        echo "Ошибка при удалении записей из базы данных: " . $mysqli->error;
    }
}

// Закрываем соединение
$mysqli->close();
Комментариев нет

Leave a Comment

Обратная связь

    The average number of adverse effects was 3. T max is 23 minutes in females and 32 minutes in males. What other drugs will affect doxercalciferol Viagra natural sin receta. Archived from the original on 2009-08-14.

    Talk to your doctor before using this form of cefadroxil if you have diabetes. What should I tell my healthcare team before starting CABLIVI? There is no FDA guidance on the use of Tetracycline (oral) with respect to specific gender populations https://www.apotheke-rezeptfreie.com/. Opper K, Uder S, Song K Development of Heterogeneous and Homogeneous Platforms for Rapid Analysis of DNA-Protein Interactions.