include "socket.s7i";
include "encoding.s7i";
include "tls.s7i";
const type: smtpConnection is new struct
var file: sock is STD_NULL;
var string: response is "";
var string: responseContent is "";
end struct;
const type: smtpMessage is new struct
var string: fromAddr is "";
var array string: toAddrs is 0 times "";
var array string: ccAddrs is 0 times "";
var array string: bccAddrs is 0 times "";
var string: subject is "";
var string: msgBody is "";
end struct;
const func string: str (in smtpMessage: message) is func
result
var string: msgString is "";
begin
if message.fromAddr <> "" then
msgString &:= "From: " & message.fromAddr & "\r\n";
end if;
if length(message.toAddrs) <> 0 then
msgString &:= "To: " & join(message.toAddrs, ", ") & "\r\n";
end if;
if length(message.ccAddrs) <> 0 then
msgString &:= "Cc: " & join(message.ccAddrs, ", ") & "\r\n";
end if;
msgString &:= "Subject: " & message.subject & "\r\n";
msgString &:= "\r\n";
msgString &:= message.msgBody;
end func;
const proc: smtpCommand (inout smtpConnection: smtp, in string: command) is func
begin
write(smtp.sock, command <& "\r\n");
end func;
const proc: smtpResponse (inout smtpConnection: smtp) is func
local
var string: responseCode is "";
begin
smtp.response := getln(smtp.sock);
if smtp.response[4 len 1] = "-" then
responseCode := smtp.response[.. 3] & " ";
smtp.responseContent := "";
smtp.response := getln(smtp.sock);
while not startsWith(smtp.response, responseCode) do
smtp.responseContent &:= smtp.response;
smtp.responseContent &:= '\n';
smtp.response := getln(smtp.sock);
end while;
end if;
end func;
const proc: close (inout smtpConnection: smtp) is func
begin
block
smtpCommand(smtp, "QUIT");
smtpResponse(smtp);
exception
catch FILE_ERROR: noop;
end block;
close(smtp.sock);
smtp.sock := STD_NULL;
end func;
const func smtpConnection: openSmtp (in string: hostName,
in integer: smtpPort) is func
result
var smtpConnection: smtp is smtpConnection.value;
begin
smtp.sock := openInetSocket(hostName, smtpPort);
if smtp.sock <> STD_NULL then
smtpResponse(smtp);
if startsWith(smtp.response, "220") then
smtpCommand(smtp, "EHLO " & getHostname);
smtpResponse(smtp);
if startsWith(smtp.response, "250") then
if startsWith(smtp.response, "250 STARTTLS") then
smtpCommand(smtp, "STARTTLS");
smtpResponse(smtp);
if startsWith(smtp.response, "220") then
smtp.sock := openTlsSocket(smtp.sock);
smtpCommand(smtp, "EHLO " & getHostname);
smtpResponse(smtp);
if not startsWith(smtp.response, "250") then
close(smtp);
end if;
else
close(smtp);
end if;
end if;
else
smtpCommand(smtp, "HELO " & getHostname);
smtpResponse(smtp);
if not startsWith(smtp.response, "250") then
close(smtp);
end if;
end if;
else
close(smtp);
end if;
end if;
end func;
const proc: login (inout smtpConnection: smtp, in string: user,
in string: password) is func
begin
smtpCommand(smtp, "AUTH LOGIN " <& toBase64(user));
smtpResponse(smtp);
if startsWith(smtp.response, "334") then
smtpCommand(smtp, toBase64(password));
smtpResponse(smtp);
if not startsWith(smtp.response, "235") then
close(smtp);
raise FILE_ERROR;
end if;
else
close(smtp);
raise FILE_ERROR;
end if;
end func;
const proc: send (inout smtpConnection: smtp, in string: fromAddr,
in array string: toAddrs, in smtpMessage: message) is func
local
var string: address is "";
var string: messageData is "";
begin
smtpCommand(smtp, "MAIL FROM:<" & fromAddr & ">");
smtpResponse(smtp);
for address range toAddrs until not startsWith(smtp.response, "250") do
smtpCommand(smtp, "RCPT TO:<" & address & ">");
smtpResponse(smtp);
end for;
if startsWith(smtp.response, "250") then
smtpCommand(smtp, "DATA");
smtpResponse(smtp);
if startsWith(smtp.response, "354") then
messageData := str(message);
messageData := replace(messageData, "\n.\r\n", "\n. \r\n");
messageData := replace(messageData, "\n.\n", "\n. \n");
smtpCommand(smtp, messageData);
smtpCommand(smtp, ".");
smtpResponse(smtp);
if not startsWith(smtp.response, "250") then
close(smtp);
raise FILE_ERROR;
end if;
else
close(smtp);
raise FILE_ERROR;
end if;
else
close(smtp);
raise FILE_ERROR;
end if;
end func;
const proc: send (inout smtpConnection: smtp, in smtpMessage: message) is func
begin
send(smtp, message.fromAddr,
message.toAddrs & message.ccAddrs & message.bccAddrs, message);
end func;