mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
net: implementation of SMTP RFC 2821 (#5807)
This commit is contained in:
parent
c891014c77
commit
549c4f54cd
216
vlib/net/smtp/smtp.v
Normal file
216
vlib/net/smtp/smtp.v
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
module smtp
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* smtp module
|
||||||
|
* Created by: nedimf (07/2020)
|
||||||
|
*/
|
||||||
|
import net
|
||||||
|
import encoding.base64
|
||||||
|
import strings
|
||||||
|
|
||||||
|
struct Commands {
|
||||||
|
ehlo string = 'EHLO Alice\r\n'
|
||||||
|
auth_plain string = 'AUTH PLAIN '
|
||||||
|
mail_from string = 'MAIL FROM:'
|
||||||
|
recpt_to string = 'RCPT TO:'
|
||||||
|
data string = 'DATA\r\n'
|
||||||
|
subject string = 'Subject:'
|
||||||
|
end_msg string = '\r\n.\r\n'
|
||||||
|
quit string = 'QUIT\r\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Sends an email trough SMTP socket
|
||||||
|
pub fn send_mail(mailserver string, port int, username, password, subject, from, to, body, type_body string, debug bool)? {
|
||||||
|
client := connect(mailserver, port, debug)?
|
||||||
|
send_ehlo(client, debug)
|
||||||
|
auth(client, username, password, debug)
|
||||||
|
send_mailfrom(client, from, debug)
|
||||||
|
send_mailto(client, to, debug)
|
||||||
|
send_data(client, debug)
|
||||||
|
if type_body == 'html' {
|
||||||
|
send_html_body(client, subject, from, to, body, debug)
|
||||||
|
} else {
|
||||||
|
send_text_body(client, subject, from, to, body, debug)
|
||||||
|
}
|
||||||
|
send_quit(client, debug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates socket connection with TCP server on provided port and returns net.Socket
|
||||||
|
pub fn connect(mailserver string, port int, debug bool) ?(net.Socket) {
|
||||||
|
mut client := net.dial(mailserver, port)?
|
||||||
|
bytes, blen := client.recv(1024)
|
||||||
|
recv := recieved(bytes, blen)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
is_debug(debug, recv)
|
||||||
|
status := recv[..3]
|
||||||
|
if status.int() != 220 {
|
||||||
|
return error('Replay (220) from server has not been recieved.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends EHLO command to connected server. EHLO (Extended HELO) tells mailserver that our connection uses ESMTP
|
||||||
|
pub fn send_ehlo(socket net.Socket, debug bool) ?bool {
|
||||||
|
cmd := Commands{}
|
||||||
|
recv := send(socket, cmd.ehlo)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
status := recv[..3]
|
||||||
|
is_debug(debug, recv)
|
||||||
|
if status.int() != 250 {
|
||||||
|
return error('Replay (250) from server has not been recieved for EHLO.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closing the connection with server
|
||||||
|
pub fn send_quit(socket net.Socket, debug bool) ?bool {
|
||||||
|
cmd := Commands{}
|
||||||
|
recv := send(socket, cmd.quit)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
status := recv[..3]
|
||||||
|
is_debug(debug, recv)
|
||||||
|
if status.int() != 221 {
|
||||||
|
return error('Replay (221) from server has not been recieved for QUIT.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_mailfrom(socket net.Socket, from string, debug bool) ?bool {
|
||||||
|
cmd := Commands{}
|
||||||
|
recv := send(socket, cmd.mail_from + ' <$from>\r\n')
|
||||||
|
if recv.len >= 3 {
|
||||||
|
status := recv[..3]
|
||||||
|
is_debug(debug, recv)
|
||||||
|
if status.int() != 221 {
|
||||||
|
return error('Replay (221) from server has not been recieved for MAIL FROM.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_mailto(socket net.Socket, to string, debug bool) ?bool {
|
||||||
|
cmd := Commands{}
|
||||||
|
recv := send(socket, cmd.recpt_to + ' <$to>\r\n')
|
||||||
|
if recv.len >= 3 {
|
||||||
|
status := recv[..3]
|
||||||
|
is_debug(debug, recv)
|
||||||
|
if status.int() != 221 {
|
||||||
|
return error('Replay (221) from server has not been recieved for MAIL TO.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_data(socket net.Socket, debug bool) ?bool {
|
||||||
|
cmd := Commands{}
|
||||||
|
recv := send(socket, cmd.data)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
status := recv[..3]
|
||||||
|
is_debug(debug, recv)
|
||||||
|
if status.int() != 354 {
|
||||||
|
return error('Replay (354) from server has not been recieved for DATA.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(socket net.Socket, string_to_send string) string {
|
||||||
|
socket.send_string(string_to_send)
|
||||||
|
bytes, blen := socket.recv(1024)
|
||||||
|
return recieved(bytes, blen)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_text_body(socket net.Socket, subject, from, to, body string, debug bool) ? {
|
||||||
|
socket.send_string('From: $from\r\n')
|
||||||
|
socket.send_string('To: $to\r\n')
|
||||||
|
socket.send_string('Subject: $subject\r\n')
|
||||||
|
socket.send_string('\r\n\r\n')
|
||||||
|
socket.send_string(body)
|
||||||
|
socket.send_string('\r\n.\r\n')
|
||||||
|
bytes, blen := socket.recv(1024)
|
||||||
|
recv := recieved(bytes, blen)
|
||||||
|
println(recv)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
status := recv[..3]
|
||||||
|
is_debug(debug, recv)
|
||||||
|
if status.int() != 250 {
|
||||||
|
return error('Replay (250) from server has not been recieved for EHLO.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
println('V: Mail sent!')
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_html_body(socket net.Socket, subject, from, to, body string, debug bool) ? {
|
||||||
|
socket.send_string('From: $from\r\n')
|
||||||
|
socket.send_string('To: $to\r\n')
|
||||||
|
socket.send_string('Subject: $subject\r\n')
|
||||||
|
socket.send_string('Content-Type: text/html; charset=ISO-8859-1')
|
||||||
|
socket.send_string('\r\n\r\n')
|
||||||
|
socket.send_string(body)
|
||||||
|
socket.send_string('\r\n.\r\n')
|
||||||
|
bytes, blen := socket.recv(1024)
|
||||||
|
recv := recieved(bytes, blen)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
is_debug(debug, recv)
|
||||||
|
status := recv[..3]
|
||||||
|
if status.int() != 250 {
|
||||||
|
return error('Replay (250) from server has not been recieved for EHLO.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
println('V: Mail sent!')
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth(client net.Socket, username, password string, debug bool) ?bool {
|
||||||
|
cmd := Commands{}
|
||||||
|
mut sb := strings.new_builder(100)
|
||||||
|
sb.write_b(0)
|
||||||
|
sb.write(username)
|
||||||
|
sb.write_b(0)
|
||||||
|
sb.write(password)
|
||||||
|
mut x := sb.str()
|
||||||
|
x = base64.encode(x)
|
||||||
|
auth_cmd := cmd.auth_plain + x + '\r\n'
|
||||||
|
recv := send(client, auth_cmd)
|
||||||
|
if recv.len >= 3 {
|
||||||
|
is_debug(debug, recv)
|
||||||
|
status := recv[..3]
|
||||||
|
if status.int() != 235 {
|
||||||
|
return error('Replay (235) from server has not been recieved for AUTH.\nReplay recieved: $status')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('Recieved data from SMTP server is not returning supported values\nReturned values: $recv')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recieved(bytes byteptr, blen int) string {
|
||||||
|
return tos(bytes, blen)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_debug(debug bool, pr string) {
|
||||||
|
if debug == true {
|
||||||
|
println('\n...\n')
|
||||||
|
println(pr)
|
||||||
|
}
|
||||||
|
}
|
18
vlib/net/smtp/smtp_test.v
Normal file
18
vlib/net/smtp/smtp_test.v
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import smtp
|
||||||
|
|
||||||
|
fn test_smtp() {
|
||||||
|
|
||||||
|
server := 'smtp.mailtrap.io'
|
||||||
|
port := 2525
|
||||||
|
username := '46d1daf3ff04f'
|
||||||
|
password := '1e8ba2dbf19f4f'
|
||||||
|
subject := 'Hello from V'
|
||||||
|
from := 'dev@vlang.io'
|
||||||
|
to := 'dev@vlang.io'
|
||||||
|
msg := '<h1>Hi,from V module, this message was sent by SMTP!</h1>'
|
||||||
|
body_type := 'html'
|
||||||
|
debug := true //use while debugging
|
||||||
|
|
||||||
|
smtp.send_mail(server, port, username, password, subject, from, to, msg, body_type,
|
||||||
|
debug)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user