san7667 писал(а):Добрый день, подскажите пожалуйста кто знает, живой действующий пример отправки сообщения на почту email самый простой..
какой-нибудь алгоритм или процедуру.
Неожиданно для себя тоже не нашел в интернете актуальных примеров на free pascal отправки простого почтового сообщения по smtp без использования TLS.
Написал простой кроссплатформенный пример на чистом fcl (без использования Indy, Synapse и тп.), так как все необходимое есть и хорошо работает очень давно.
Компилировал и проверял под Windows 10 и под Linux Mint (Ubuntu).
- Код: Выделить всё
program socketmail;
{$mode objfpc}{$H+}
uses
sockets, // работа с Sockets
resolve, // Преобразование доменных имен в IP адреса и обратно с помощью DNS
base64; // base64 кодирование/декодирование
function SendMail(const SmtpServer, SenderEmail, Password, RecipientEmail, MailSubject, MailBody: string; SmtpPort: Integer): Boolean;
var
CSocket: TSocket;
Address: TInetSockAddr;
Buffer: array[0..1023] of Char;
BytesRead: Integer;
IPAddr: string;
MailContent, Base64Encoded: string;
hrs: THostResolver;
procedure SendCommand(Socket: TSocket; const Command: string);
begin
WriteLn('Client: ', Command);
fpsend(Socket, PChar(Command + #13#10), longword(Length(Command) + 2), 0);
BytesRead := fprecv(Socket, @Buffer[0], SizeOf(Buffer), 0);
Buffer[BytesRead] := #0;
WriteLn('Server: ', Buffer);
end;
begin
Result := False; // По умолчанию считаем, что отправка не удалась
// Создание сокета
CSocket := fpsocket(AF_INET, SOCK_STREAM, 0);
if CSocket = -1 then
begin
WriteLn('Error creating socket.');
Exit;
end;
// Преобразование доменного имени SMTP сервера в IP адрес
hrs := THostResolver.Create(nil);
if hrs.NameLookup(SmtpServer) then
begin
IPAddr := hrs.AddressAsString;
WriteLn('Resolved IP Address: ', IPAddr);
end
else
begin
WriteLn('Failed to resolve the hostname: ', SmtpServer);
Exit;
end;
// Подключение к SMTP серверу
with Address do
begin
sin_family := AF_INET; //TCP/IP
sin_port:= htons(word(SmtpPort)); //Порт
sin_addr:=StrToNetAddr(hrs.AddressAsString); // IP адрес
end;
hrs.Free; // Освобождаем переменную
if fpconnect(CSocket, @Address, SizeOf(Address)) < 0 then
begin
WriteLn('Error connecting to SMTP server.');
Exit;
end;
// Отправка команды EHLO
SendCommand(CSocket, 'EHLO client');
// Производим аутентификацию, отправляя команды AUTH LOGIN
SendCommand(CSocket, 'AUTH LOGIN');
// Отправка логина (email отправителя) в формате Base64
Base64Encoded := EncodeStringBase64(SenderEmail);
if Base64Encoded = '' then
begin
WriteLn('Error encoding login.');
Exit;
end;
SendCommand(CSocket, Base64Encoded);
// Отправка пароля в формате Base64
Base64Encoded := EncodeStringBase64(Password);
if Base64Encoded = '' then
begin
WriteLn('Error encoding password.');
Exit;
end;
SendCommand(CSocket, Base64Encoded);
// Отправка команды MAIL FROM
MailContent := 'MAIL FROM: <' + SenderEmail + '>';
SendCommand(CSocket, MailContent);
// Отправка команды RCPT TO
MailContent := 'RCPT TO: <' + RecipientEmail + '>';
SendCommand(CSocket, MailContent);
// Отправка команды DATA
SendCommand(CSocket, 'DATA');
// Отправка данных письма
MailContent :=
'Subject: ' + MailSubject + #13#10 +
'From: <' + SenderEmail + '>' + #13#10 +
'To: <' + RecipientEmail + '>' + #13#10 +
#13#10 +
MailBody + #13#10 +
'.';
SendCommand(CSocket, MailContent);
// Отправка команды QUIT
SendCommand(CSocket, 'QUIT');
// Закрытие сокета
CloseSocket(CSocket);
Result := True; // Отправка прошла успешно
end;
begin
if SendMail('smtp.rambler.ru', 'yourlogin@rambler.ru', 'yourpassword', 'adressto@mail.com', 'Test Mail', 'This is a test email sent using sockets.', 25) then
WriteLn('Mail sent successfully.')
else
WriteLn('Error sending mail.');
end.
а также пример на чистом fcl под windows с помощью WinSock2:
- Код: Выделить всё
program socketmail;
{$mode objfpc}{$H+}
uses
WinSock, // WinSock2 Socket Library for Win32
base64; // base64 encoder & decoder
function SendMail(const SmtpServer, SenderEmail, Password, RecipientEmail, MailSubject, MailBody: string; SmtpPort: Integer): Boolean;
var
CSocket: TSocket;
Address: TSockAddrIn;
Buffer: array[0..1023] of Char;
BytesRead: Integer;
IPAddr: string;
HostEnt: PHostEnt;
Addr: TInAddr;
WSAData: TWSAData;
MailContent, Base64Encoded: string;
procedure SendCommand(Socket: TSocket; const Command: string);
begin
WriteLn('Client: ', Command);
Send(Socket, PChar(Command + #13#10), Length(Command) + 2, 0);
BytesRead := Recv(Socket, Buffer[0], SizeOf(Buffer), 0);
Buffer[BytesRead] := #0;
WriteLn('Server: ', Buffer);
end;
begin
Result := False; // По умолчанию считаем, что отправка не удалась
// Инициализация WinSock
if WSAStartup($202, WSAData) <> 0 then
begin
WriteLn('Error initializing WinSock.');
Exit;
end;
// Создание сокета
CSocket := Socket(AF_INET, SOCK_STREAM, 0);
if CSocket = INVALID_SOCKET then
begin
WriteLn('Error creating socket.');
Exit;
end;
// Преобразование доменного имени SMTP сервера в IP адрес
HostEnt := gethostbyname(PChar(SmtpServer));
if HostEnt <> nil then
begin
Addr := PInAddr(HostEnt^.h_addr_list^)^;
IPAddr := inet_ntoa(Addr);
end
else
begin
WriteLn('Host not found: ' + SmtpServer);
Exit;
end;
// Подключение к SMTP серверу
Address.sin_family := AF_INET;
Address.sin_port := htons(u_short(SmtpPort));
Address.sin_addr.s_addr := inet_addr(PChar(IPAddr));
if connect(CSocket, Address, SizeOf(Address)) < 0 then
begin
WriteLn('Error connecting to SMTP server.');
Exit;
end;
// Отправка команды EHLO
SendCommand(CSocket, 'EHLO client');
// Производим аутентификацию, отправляя команды AUTH LOGIN
SendCommand(CSocket, 'AUTH LOGIN');
// Отправка логина (email отправителя) в формате Base64
Base64Encoded := EncodeStringBase64(SenderEmail);
if Base64Encoded = '' then
begin
WriteLn('Error encoding login.');
Exit;
end;
SendCommand(CSocket, Base64Encoded);
// Отправка пароля в формате Base64
Base64Encoded := EncodeStringBase64(Password);
if Base64Encoded = '' then
begin
WriteLn('Error encoding password.');
Exit;
end;
SendCommand(CSocket, Base64Encoded);
// Отправка команды MAIL FROM
MailContent := 'MAIL FROM: <' + SenderEmail + '>';
SendCommand(CSocket, MailContent);
// Отправка команды RCPT TO
MailContent := 'RCPT TO: <' + RecipientEmail + '>';
SendCommand(CSocket, MailContent);
// Отправка команды DATA
SendCommand(CSocket, 'DATA');
// Отправка данных письма
MailContent :=
'Subject: ' + MailSubject + #13#10 +
'From: <' + SenderEmail + '>' + #13#10 +
'To: <' + RecipientEmail + '>' + #13#10 +
#13#10 +
MailBody + #13#10 +
'.';
SendCommand(CSocket, MailContent);
// Отправка команды QUIT
SendCommand(CSocket, 'QUIT');
// Закрытие сокета
CloseSocket(CSocket);
// Очистка WinSock
WSACleanup;
Result := True; // Отправка прошла успешно
end;
begin
if SendMail('smtp.rambler.ru', 'yourlogin@rambler.ru', 'yourpassword', 'adressto@mail.com', 'Test Mail', 'This is a test email sent using sockets.', 25) then
WriteLn('Mail sent successfully.')
else
WriteLn('Error sending mail.');
end.
Оба примера отлично отправляют сообщения на любые адреса через почту Rambler.
По поводу мнений о том, что все крупные почтовые сервисы требуют безопасного подключения по smtp, зарегистрировал бесплатный ящик на
Rambler, в помощи
написано, что подключение по smtp может осуществляться на smtp.rambler.ru в том числе без шифрования. Главное, не забыть включить в настройках почты переключатель "Доступ к почтовому ящику с помощью почтовых клиентов."
Тестовые сообщения успешно отправляются на любые адреса.
Понятно, что это просто пример, но он позволяет понять "кухню" изнутри...
При необходимости, можно расширить для отправки вложений и т.п., добавить обработку ответов с сервера.
Если нужен позарез TLS, то можно конечно заморочиться с
Schannel (MS SSPI SSL), но OpenSSL будет гораздо проще, ценой двух dll в папке с программой.
Приведенные примеры сделаны в процедурном стиле с помощью модуля
sockets из состава rtl, если требуется объектно-ориентрованный подход, то можно использовать надстройку над sockets (для windows она использует winsock) из состава fpc-net модуль
sscockets.
Модуль "sockets" из состава RTL (Run-Time Library) и модуль "ssockets" из состава FCL-Net (Free Component Library for Network) предоставляют функциональность для работы с сокетами. Однако есть некоторые различия между ними:
1. Модуль "sockets" из состава RTL:
- Этот модуль предоставляет базовые функции для работы с сокетами, которые являются стандартными для языка Free Pascal.
- Включает простые процедуры и функции для создания, связывания, прослушивания и подключения сокетов.
- Модуль "sockets" предоставляет сокеты на низком уровне, что означает, что вам придется самостоятельно управлять деталями работы с сокетами, такими как настройка соединения и обработка данных.
2. Модуль "ssockets" из состава FCL-Net:
- Этот модуль также предоставляет функции для работы с сокетами, но на более высоком уровне абстракции.
- Включает классы и компоненты для работы с сетевыми протоколами, такими как TCP и UDP, а также предоставляет удобные методы для обмена данными.
- Модуль "ssockets" предоставляет более удобный и объектно-ориентированный интерфейс, что делает код более читаемым и позволяет сосредотачиваться на бизнес-логике, а не на низкоуровневых деталях работы с сокетами.
Основное различие между модулем "sockets" из RTL и модулем "ssockets" из FCL-Net заключается в уровне абстракции и удобстве использования. Модуль "sockets" предоставляет базовую функциональность на низком уровне, в то время как модуль "ssockets" абстрагирует сложности работы с сокетами и предоставляет более удобный интерфейс для программистов, что облегчает создание сетевых приложений на Free Pascal.
В ходе изысканий использовал
smtp4dev - фейковый smtp email server для разработки и тестирования
Наткнулся на интересный
набор маленьких утилит для отправки почты, в том числе из консоли
И попалась интересная программа
Stunnel - прокси, предназначенная для добавления функциональности TLS кодирования для существующих клиентов и серверов, без изменения их кода.
Ну и как еще одно решение - использование smtp релея, своего или стороннего.