Форум: "WinAPI";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.03.18;
Скачать: [xml.tar.bz2];




Вниз

E-Mail & Winsock. 


BWG   (2002-01-14 19:00) [0]

Мастера!
Понимаю, что тема избита, но так никто толком не ответил на вопрос: как написать процедуру отправки E-Mail, используя один только Winsock API. Помогите чем можите!



BWG   (2002-01-15 04:53) [1]

Неужели никто не знает?
Или просто лень ответить?



paul_shmakov   (2002-01-15 11:07) [2]

вот, только что набросал. пример показывает посылку самого простого письма. остальные поля в заголовке (типа Subject) и диалог с smtp сервером см. в rfc.
да и коды, которые сервер возвращает - это я так, на примере сейчас посмотрел - точно не помню. так что за дополнительной информацией RFC 0821.

program mail;

uses Windows, SysUtils, Winsock;

function SendMail(const From: string; const Receiver: string; const Text: string;
const SmtpHost: string; SmtpPort: Word = 25): Boolean;
var
S: TSocket;
WSData: TWSAData;

function ConnectToSmtp: Boolean;
var
Sin: SockAddr_in;
IP: DWord;
He: PHostent;
begin
Result := false;

IP := inet_addr(PChar(SmtpHost));

if INADDR_NONE = IP then
begin
He := gethostbyname(PChar(SmtpHost));
if not Assigned(He) then
Exit;
IP := PDWord(PDWord(He^.h_addr_list)^)^;
end;

S := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if INVALID_SOCKET <> S then
begin
FillChar(Sin, SizeOf(Sin), 0);
Sin.sin_family := AF_INET;
Sin.sin_port := htons(SmtpPort);
Sin.sin_addr.s_addr := IP;

Result := SOCKET_ERROR <> connect(S, Sin, SizeOf(Sin));

if not Result then
closesocket(S);
end;
end;

function SendCommand(Text: string): Boolean;
var
Buffer: PChar;
Size: Integer;
begin
Text := Text + #10;
Size := Length(Text);
GetMem(Buffer, Size + 1);
try
StrCopy(Buffer, PChar(Text));
Result := send(S, Buffer^, Size, 0) <> SOCKET_ERROR;
finally
FreeMem(Buffer);
end;
end;

function ReceiveResponse: Integer;
var
Response: string;
C: Char;
begin
Result := 0;

Response := "";
repeat
if SOCKET_ERROR = recv(S, C, SizeOf(C), 0) then
Exit;

Response := Response + C;
until C = #10;

if Pos(" ", Response) > 0 then
Result := StrToIntDef(Copy(Response, 1, Pos(" ", Response) - 1), 0)
else
Result := StrToIntDef(Response, 0);
end;

begin
Result := false;
WSAStartup($0101, WSData);
try
if ConnectToSmtp then
try
ReceiveResponse;
Result :=
SendCommand("helo delphimail") and (250 = ReceiveResponse) and
SendCommand("mail from: " + From) and (250 = ReceiveResponse) and
SendCommand("rcpt to: " + Receiver) and (250 = ReceiveResponse) and
SendCommand("data") and (354 = ReceiveResponse) and
SendCommand(Text + #10"."#10) and (250 = ReceiveResponse) and
SendCommand("quit") and (250 = ReceiveResponse);
finally
closesocket(S);
end;
finally
WSACleanup;
end;
end;

begin
SendMail("<billy@microsoft.com>", "<paul_shmakov@mail.ru>", "Hello from Billy!", "smtp.mail.ru");
end.



BWG   (2002-01-16 17:20) [3]

Не работает этот код. Уже застрелился понимать почему. Исправил в function SendCommand(Text: string): Boolean; строку Text := Text + #10; на Text := Text + №13#10; , строку SendMail("<billy@microsoft.com>", "<paul_shmakov@mail.ru>", "Hello from Billy!", "smtp.mail.ru"); на строку SendMail("<one@prog.ru>", "<bwgblackraven@narod.ru>", "Date: "+ DateTimeToStr(now)+#13#10+"From: one@prog.ru"+#13#10+ "To: <bwgblackraven@narod.ru>"+#13#10+"Subject: Mail"+ #13#10#13#10+"The test", "smtp.narod.ru") . Все равно не работает!

Помогите понять: ПОЧЕМУ?



Polevi   (2002-01-17 08:43) [4]

Коды возврата проверял ?



BWG   (2002-01-17 13:35) [5]

Нет вроде. Надо бы... Пошел проверять...



Polevi   (2002-01-17 13:43) [6]

У меня работает без всяких исправлений (кроме аргументов ф-ии)



paul_shmakov   (2002-01-17 13:45) [7]

может не работать по той причине, что smtp.narod.ru не позволяет вход без авторизации. попробуй telnet-ом туда подсоединиться. диалог у вас с ним примерно такой должен получиться:

220 ESMTP MAIL.RU Thu, 17 Jan 2002 13:31:42 +0300
helo delphimail
250 mx10.port.ru Hello delphimail [10.0.0.1]
mail from: <billy@microsoft.com>
250 <billy@microsoft.com> is syntactically correct
rcpt to: <paul_shmakov@mail.ru>
250 <paul_shmakov@mail.ru> verified
data
354 Enter message, ending with "." on a line by itself
hello from billy!!!
.
250 OK id=16R9qY-0007Ut-00
quit
221 mx10.port.ru closing connection

если это пройдет, то значит и программа должна работать. приведенная выше функция - это реализация этого диалога на сокетах.
правда, скорее всего потребуется авторизация :(

кстати, то, что Вы исправили #10 на #13#10 - это правильно (сейчас rfc посмотрел).

SMTP RFC 0821
http://www.ietf.org/rfc/rfc0821.txt?number=0821



paul_shmakov   (2002-01-17 13:57) [8]

ну и на любителей более новая версия протокола:
smtp rfc 2821
http://www.ietf.org/rfc/rfc2821.txt?number=2821



BWG   (2002-01-17 14:24) [9]

Протестировал так:
program TestMail;
{$APPTYPE CONSOLE}
uses
SysUtils, Winsock, Windows;

function SendMail(const From: string; const Receiver: string; const Text: string;
const SmtpHost: string; SmtpPort: Word = 25): Boolean;
var
S: TSocket;
WSData: TWSAData;

function ConnectToSmtp: Boolean;
var
Sin: SockAddr_in;
IP: DWord;
He: PHostent;
begin
Result := false;

IP := inet_addr(PChar(SmtpHost));

if INADDR_NONE = IP then
begin
He := gethostbyname(PChar(SmtpHost));
if not Assigned(He) then
Exit;
IP := PDWord(PDWord(He^.h_addr_list)^)^;
writeln("IP is ",IP);
end;

S := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if INVALID_SOCKET <> S then
begin
FillChar(Sin, SizeOf(Sin), 0);
Sin.sin_family := AF_INET;
Sin.sin_port := htons(SmtpPort);
Sin.sin_addr.s_addr := IP;

Result := SOCKET_ERROR <> connect(S, Sin, SizeOf(Sin));
if Result then writeln("Connected ok");

if not Result then
closesocket(S);
end;
end;

function SendCommand(Text: string): Boolean;
var
Buffer: PChar;
Size: Integer;
begin
Text := Text + #13#10;
Size := Length(Text);
GetMem(Buffer, Size + 1);
try
StrCopy(Buffer, PChar(Text));
Result := send(S, Buffer^, Size, 0) <> SOCKET_ERROR;
writeln("command > ",text," > result > ",result);
finally
FreeMem(Buffer);
end;
end;

function ReceiveResponse: Integer;
var
Response: string;
C: Char;
begin
Result := 0;

Response := "";
repeat
if SOCKET_ERROR = recv(S, C, SizeOf(C), 0) then
begin
writeln("Socket error!");
Exit;
end;

Response := Response + C;
until C = #10;

if Pos(" ", Response) > 0 then
Result := StrToIntDef(Copy(Response, 1, Pos(" ", Response) - 1), 0)
else
Result := StrToIntDef(Response, 0);
writeln("Resp. result > ",result);
end;

begin
Result := false;
WSAStartup($0101, WSData);
try
if ConnectToSmtp then
try
ReceiveResponse;
//Result :=
SendCommand(#32+"helo msiers");// and (250 = ReceiveResponse) and
ReceiveResponse;
SendCommand("mail from: " + From);// and (250 = ReceiveResponse) and
ReceiveResponse;
SendCommand("rcpt to: " + Receiver);// and (250 = ReceiveResponse) and
ReceiveResponse;
SendCommand("data");// and (354 = ReceiveResponse) and
ReceiveResponse;
SendCommand(Text + #13#10+".");// and (250 = ReceiveResponse) and
ReceiveResponse;
SendCommand("quit");// and (250 = ReceiveResponse);
finally
closesocket(S);
end;
finally
WSACleanup;
end;
end;

begin
writeln("Started");
SendMail("<bwgblackraven@narod.ru>", "<bwgblackraven@narod.ru>", "Date: "+
DateTimeToStr(now)+#13#10+"From: one@prog.ru"+#13#10+
"To: <bwgblackraven@narod.ru>"+#13#10+"Subject: Mail"+
#13#10#13#10+"The test", "smtp.narod.ru");
writeln("Sended");
readln;
end.


Получил такой пинок:

220
550
503
503
503
550


По RFC 503 - Bad sequence of commands

И что не верно?



BWG   (2002-01-17 14:27) [10]

Смена сервера на smtp.yandex.ru не помогла. Хотя он авторизации точно не требует.



Polevi   (2002-01-17 14:31) [11]

Ты пробовал тоже самое сделать телнетом ?



BWG   (2002-01-17 16:11) [12]

Что "то же самое"?



paul_shmakov   (2002-01-17 16:22) [13]

503 - это уже после. главное здесь 550

550 Requested action not taken: mailbox unavailable
[E.g., mailbox not found, no access]

к сожалению, почему-то у меня сейчас не получается соединиться telnet-ом ни с smtp.narod.ru, ни с smtp.yandex.ru - толи сервера висят, толи файервол у нас не пускает, хотя с smtp.mail.ru все нормально.



BWG   (2002-01-17 16:31) [14]

Все Яндекс к фене полетел...



paul_shmakov   (2002-01-17 16:37) [15]

2 BWG © (17.01.02 16:11)

Что "то же самое"?
подключиться telnet-ом к smtp.narod.ru на 25 порт и ввести вручную все команды (перед этим не забудьте включить Local Echo в Terminal/Preferences).
ваш возможный диалог с сервером я уже приводил чуть выше (paul_shmakov © (17.01.02 13:45)).
если руками все это получиться, то и программно все должно работать.




BWG   (2002-01-17 17:06) [16]

Не пойму чего-то. То ли получается, то ли нет. Может у меня ручки кривые.
Попробуйте, please, кто-нибудь вышеуказанный BWG © (17.01.02 14:24) код и намыльте о результатах. Не работает он у меня.
И телнет говорит " 550" когда я mail from отправляю.
Прочитайте мне, please, manual.

С уважением, Алексей.



Bachin   (2002-01-17 18:02) [17]

Уменя такой диалог. Только не пойму, где он пустую команду откопал :(

220 profix.kiev.ua ESMTP Sendmail 8.12.1/8.12.1; Thu, 17 Jan 2002 16:59:17 +0200 (EET)
helo delphimail
250 profix.kiev.ua Hello bachin.profix.com [196.1.4.13], pleased to meet you
mail from: <bachin@prifix.kiev.ua>
250 2.1.0 <bachin@prifix.kiev.ua>... Sender ok
rcpt to: <bachin@usb.kherson.ua>
250 2.1.5 <bachin@usb.kherson.ua>... Recipient ok
data
354 Enter mail, end with "." on a line by itself
test message
.

250 2.0.0 g0HExHVN029445 Message accepted for delivery
quit
500 5.5.1 Command unrecognized: ""
FALSE



paul_shmakov   (2002-01-17 18:33) [18]

2 Bachin:
все нормально, вот только что-то он на quit ругнулся (???)
а в остальном все именно так и должно быть



BWG   (2002-01-17 21:50) [19]

Ладно.
Пошел разбираться.



BWG   (2002-01-18 20:50) [20]

Заставить работать Яндекс ( smtp.yandex.ru port 25) удалось. Отправил все без вопросов. Этот код - ни в какую не хочет работать!



BWG   (2002-01-19 12:10) [21]

Кстати: подскажите хороший Telnet-клиент (F reeware).



Anatoly Podgoretsky   (2002-01-19 19:14) [22]

Start|Run|Telnet



BWG   (2002-01-19 19:29) [23]

2 Anatoly Podgoretsky © (19.01.02 19:14) Это хороший, да? Я даже оттуда лог не могу скопировать!

Так вот, повторяю свою мысль. По словам Павла Шумакова если с помощью телнета удалось подулючиться - значит и код будет работать. С помощью телнета подключиться удалось. Код - ни в какую!

Помогите!!!



Anatoly Podgoretsky   (2002-01-19 21:48) [24]

Для того что бы сказать хороший или нет сперва надо дать определение этого. Меня в большинстве случаев устраивает, в некоторых других я использую TereTerm, но чаще именно первый.



Ghost.   (2002-01-19 21:55) [25]

Вообще 550 озачает, что почтовый ящик недоступен..ты навертное это знаешь и без меня, а на счет телнета мысль очень хорошая я тоже так делал...когда отпртавлял через маил ру и только с помощью телнета разобрался, чо если отправляешь не с их ящика или не на их ящик север возращает ошибку 553 и оказалось, что если ввести любой адрес (чужой) но их сертвера все пройдет нормально и получатель получит письмо с левого ящика..попробуй телетом всю тртанзакцию провести



paul_shmakov   (2002-01-20 01:56) [26]

2 BWG:
Так вот, повторяю свою мысль. По словам Павла Шумакова если с помощью телнета
Шмаков я :)

удалось подулючиться - значит и код будет работать. С помощью телнета подключиться удалось. Код - ни в какую!

ну запустите под отладчиком и оттрассируйте. код делает (должен делать) именно то, что и человек, набирающий команды в окне telnet-терминала.
код вполне может глючить - я его за пару минут написал специально для ответа на Ваш вопрос.



BWG   (2002-01-20 08:31) [27]

Хорошо, хорошо.
Сейчас полностью оттестирую и тогда выложу все результаты.




Форум: "WinAPI";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.03.18;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.79 MB
Время: 0.048 c
1-57462           vopros                2002-03-04 10:24  2002.03.18  
Опять TMemo


14-57504          Феликс                2002-02-03 00:50  2002.03.18  
Пара простых вопросов по Перлу.


3-57333           Андре                 2002-02-20 08:58  2002.03.18  
Поиск по базе


3-57316           boogier               2002-02-19 12:25  2002.03.18  
Можно ли при помощи выполнить запрос асинхронно?


6-57474           АндрейП               2002-01-02 17:03  2002.03.18  
TWebBrowser - использование UrlMkSetSessionOption