diff options
| author | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
|---|---|---|
| committer | Indrajith K L | 2022-12-03 17:00:20 +0530 | 
| commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
| tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/old/vlib/net/smtp | |
| download | cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2 cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.zip | |
Diffstat (limited to 'v_windows/v/old/vlib/net/smtp')
| -rw-r--r-- | v_windows/v/old/vlib/net/smtp/smtp.v | 190 | ||||
| -rw-r--r-- | v_windows/v/old/vlib/net/smtp/smtp_test.v | 89 | 
2 files changed, 279 insertions, 0 deletions
| diff --git a/v_windows/v/old/vlib/net/smtp/smtp.v b/v_windows/v/old/vlib/net/smtp/smtp.v new file mode 100644 index 0000000..50c537c --- /dev/null +++ b/v_windows/v/old/vlib/net/smtp/smtp.v @@ -0,0 +1,190 @@ +module smtp + +/* +* +* smtp module +* Created by: nedimf (07/2020) +*/ +import net +import encoding.base64 +import strings +import time +import io + +const ( +	recv_size = 128 +) + +enum ReplyCode { +	ready = 220 +	close = 221 +	auth_ok = 235 +	action_ok = 250 +	mail_start = 354 +} + +pub enum BodyType { +	text +	html +} + +pub struct Client { +mut: +	conn   net.TcpConn +	reader io.BufferedReader +pub: +	server   string +	port     int = 25 +	username string +	password string +	from     string +pub mut: +	is_open bool +} + +pub struct Mail { +	from      string +	to        string +	cc        string +	bcc       string +	date      time.Time = time.now() +	subject   string +	body_type BodyType +	body      string +} + +// new_client returns a new SMTP client and connects to it +pub fn new_client(config Client) ?&Client { +	mut c := &Client{ +		...config +	} +	c.reconnect() ? +	return c +} + +// reconnect reconnects to the SMTP server if the connection was closed +pub fn (mut c Client) reconnect() ? { +	if c.is_open { +		return error('Already connected to server') +	} + +	conn := net.dial_tcp('$c.server:$c.port') or { return error('Connecting to server failed') } +	c.conn = conn + +	c.reader = io.new_buffered_reader(reader: c.conn) + +	c.expect_reply(.ready) or { return error('Received invalid response from server') } +	c.send_ehlo() or { return error('Sending EHLO packet failed') } +	c.send_auth() or { return error('Authenticating to server failed') } +	c.is_open = true +} + +// send sends an email +pub fn (mut c Client) send(config Mail) ? { +	if !c.is_open { +		return error('Disconnected from server') +	} +	from := if config.from != '' { config.from } else { c.from } +	c.send_mailfrom(from) or { return error('Sending mailfrom failed') } +	c.send_mailto(config.to) or { return error('Sending mailto failed') } +	c.send_data() or { return error('Sending mail data failed') } +	c.send_body(Mail{ +		...config +		from: from +	}) or { return error('Sending mail body failed') } +} + +// quit closes the connection to the server +pub fn (mut c Client) quit() ? { +	c.send_str('QUIT\r\n') ? +	c.expect_reply(.close) ? +	c.conn.close() ? +	c.is_open = false +} + +// expect_reply checks if the SMTP server replied with the expected reply code +fn (mut c Client) expect_reply(expected ReplyCode) ? { +	bytes := io.read_all(reader: c.conn) ? + +	str := bytes.bytestr().trim_space() +	$if smtp_debug ? { +		eprintln('\n\n[RECV]') +		eprint(str) +	} + +	if str.len >= 3 { +		status := str[..3].int() +		if ReplyCode(status) != expected { +			return error('Received unexpected status code $status, expecting $expected') +		} +	} else { +		return error('Recieved unexpected SMTP data: $str') +	} +} + +[inline] +fn (mut c Client) send_str(s string) ? { +	$if smtp_debug ? { +		eprintln('\n\n[SEND START]') +		eprint(s.trim_space()) +		eprintln('\n[SEND END]') +	} +	c.conn.write(s.bytes()) ? +} + +[inline] +fn (mut c Client) send_ehlo() ? { +	c.send_str('EHLO $c.server\r\n') ? +	c.expect_reply(.action_ok) ? +} + +[inline] +fn (mut c Client) send_auth() ? { +	if c.username.len == 0 { +		return +	} +	mut sb := strings.new_builder(100) +	sb.write_b(0) +	sb.write_string(c.username) +	sb.write_b(0) +	sb.write_string(c.password) +	a := sb.str() +	auth := 'AUTH PLAIN ${base64.encode_str(a)}\r\n' +	c.send_str(auth) ? +	c.expect_reply(.auth_ok) ? +} + +fn (mut c Client) send_mailfrom(from string) ? { +	c.send_str('MAIL FROM: <$from>\r\n') ? +	c.expect_reply(.action_ok) ? +} + +fn (mut c Client) send_mailto(to string) ? { +	c.send_str('RCPT TO: <$to>\r\n') ? +	c.expect_reply(.action_ok) ? +} + +fn (mut c Client) send_data() ? { +	c.send_str('DATA\r\n') ? +	c.expect_reply(.mail_start) ? +} + +fn (mut c Client) send_body(cfg Mail) ? { +	is_html := cfg.body_type == .html +	date := cfg.date.utc_string().trim_right(' UTC') // TODO +	mut sb := strings.new_builder(200) +	sb.write_string('From: $cfg.from\r\n') +	sb.write_string('To: <$cfg.to>\r\n') +	sb.write_string('Cc: <$cfg.cc>\r\n') +	sb.write_string('Bcc: <$cfg.bcc>\r\n') +	sb.write_string('Date: $date\r\n') +	sb.write_string('Subject: $cfg.subject\r\n') +	if is_html { +		sb.write_string('Content-Type: text/html; charset=ISO-8859-1') +	} +	sb.write_string('\r\n\r\n') +	sb.write_string(cfg.body) +	sb.write_string('\r\n.\r\n') +	c.send_str(sb.str()) ? +	c.expect_reply(.action_ok) ? +} diff --git a/v_windows/v/old/vlib/net/smtp/smtp_test.v b/v_windows/v/old/vlib/net/smtp/smtp_test.v new file mode 100644 index 0000000..d975e57 --- /dev/null +++ b/v_windows/v/old/vlib/net/smtp/smtp_test.v @@ -0,0 +1,89 @@ +import os +import net.smtp +import time + +// Used to test that a function call returns an error +fn fn_errors(mut c smtp.Client, m smtp.Mail) bool { +	c.send(m) or { return true } +	return false +} + +/* +* +* smtp_test +* Created by: nedimf (07/2020) +*/ +fn test_smtp() { +	$if !network ? { +		return +	} + +	client_cfg := smtp.Client{ +		server: 'smtp.mailtrap.io' +		from: 'dev@vlang.io' +		username: os.getenv('VSMTP_TEST_USER') +		password: os.getenv('VSMTP_TEST_PASS') +	} +	if client_cfg.username == '' && client_cfg.password == '' { +		eprintln('Please set VSMTP_TEST_USER and VSMTP_TEST_PASS before running this test') +		exit(0) +	} +	send_cfg := smtp.Mail{ +		to: 'dev@vlang.io' +		subject: 'Hello from V2' +		body: 'Plain text' +	} + +	mut client := smtp.new_client(client_cfg) or { +		assert false +		return +	} +	assert true +	client.send(send_cfg) or { +		assert false +		return +	} +	assert true +	client.send(smtp.Mail{ +		...send_cfg +		from: 'alexander@vlang.io' +	}) or { +		assert false +		return +	} +	client.send(smtp.Mail{ +		...send_cfg +		cc: 'alexander@vlang.io,joe@vlang.io' +		bcc: 'spytheman@vlang.io' +	}) or { +		assert false +		return +	} +	client.send(smtp.Mail{ +		...send_cfg +		date: time.now().add_days(1000) +	}) or { +		assert false +		return +	} +	assert true +	client.quit() or { +		assert false +		return +	} +	assert true +	// This call should return an error, since the connection is closed +	if !fn_errors(mut client, send_cfg) { +		assert false +		return +	} +	client.reconnect() or { +		assert false +		return +	} +	client.send(send_cfg) or { +		assert false +		return +	} +	assert true +} | 
