From 000268442417d25709bc4531e8e0556896d66dd1 Mon Sep 17 00:00:00 2001 From: "Jimi (Dimitris) Charalampidis" Date: Wed, 13 Nov 2024 19:13:22 +0200 Subject: [PATCH 1/2] Add support for STARTTLS --- lib/notifiers.ts | 8 +++++--- lib/smtp.ts | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/notifiers.ts b/lib/notifiers.ts index 7c5e0f4..7db3980 100644 --- a/lib/notifiers.ts +++ b/lib/notifiers.ts @@ -1,4 +1,4 @@ -import { SmtpClient } from "./smtp.ts"; +import { ConnectConfigWithAuthentication, SmtpClient } from "./smtp.ts"; import { getLogger } from "@std/log"; import { DockerApiContainerEvent } from "./docker-api.ts"; import { CheckedConfiguration, ConfigOption } from "./env.ts"; @@ -107,8 +107,10 @@ export class SmtpNotifier extends Notifier { port: SmtpNotifier.config.port, username: SmtpNotifier.config.username, password: SmtpNotifier.config.password, - }; - if (SmtpNotifier.config.use_tls) { + } as ConnectConfigWithAuthentication; + if (!SmtpNotifier.config.use_tls && connection_config.username !== undefined) { + await client.connect_starttls(connection_config); + } else if (SmtpNotifier.config.use_tls) { await client.connect_tls(connection_config); } else { await client.connect_plain(connection_config); diff --git a/lib/smtp.ts b/lib/smtp.ts index 5c41865..22ea48a 100644 --- a/lib/smtp.ts +++ b/lib/smtp.ts @@ -24,7 +24,7 @@ type ConnectConfig = { port?: number; }; -type ConnectConfigWithAuthentication = ConnectConfig & { +export type ConnectConfigWithAuthentication = ConnectConfig & { username: string; password: string; }; @@ -64,6 +64,14 @@ export class SmtpClient { await this.connect(connection, config); } + async connect_starttls(config: ConnectConfigWithAuthentication) { + const connection = await Deno.connect({ + hostname: config.hostname, + port: config.port || 587, + }); + await this.connect(connection, config); + } + private async connect(connection: Deno.Conn, config: ConnectConfig) { this.connection = connection; this.reader = this.connection.readable @@ -74,11 +82,32 @@ export class SmtpClient { this.assert_code(await this.read_command(), CommandCode.READY); await this.write_command(`EHLO ${config.hostname}\r\n`); + + let supports_starttls = false; + while (true) { const command = await this.read_command(); if (!command || !command.args.startsWith("-")) break; + if (command.args.includes("STARTTLS")) { + supports_starttls = true; + } } + if (this.use_authentication(config)) { + if (!this.isTlsConn(this.connection)) { + if (!supports_starttls) { + throw new Error("STARTTLS is not supported by the server"); + } + await this.write_command(`STARTTLS\r\n`); + this.assert_code(await this.read_command(), CommandCode.READY); + this.reader.cancel(); + this.connection = await Deno.startTls(this.connection as Deno.TcpConn, { hostname: config.hostname }); + this.reader = this.connection.readable + .pipeThrough(new TextDecoderStream()) + .pipeThrough(new TextLineStream()) + .getReader(); + } + await this.write_command(`AUTH LOGIN\r\n`); this.assert_code(await this.read_command(), CommandCode.SERVER_CHALLENGE); @@ -90,6 +119,10 @@ export class SmtpClient { } } + isTlsConn(arg: Deno.Conn): arg is Deno.TlsConn { + return (arg as Deno.TlsConn).handshake !== undefined; + } + close() { if (!this.connection) { return; From 68c7450c178d5fbee1209bcd3d10ece08b95360b Mon Sep 17 00:00:00 2001 From: "Jimi (Dimitris) Charalampidis" Date: Sat, 23 Nov 2024 15:39:05 +0200 Subject: [PATCH 2/2] Apply method naming convention --- lib/smtp.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/smtp.ts b/lib/smtp.ts index 22ea48a..20abcdb 100644 --- a/lib/smtp.ts +++ b/lib/smtp.ts @@ -94,7 +94,7 @@ export class SmtpClient { } if (this.use_authentication(config)) { - if (!this.isTlsConn(this.connection)) { + if (!this.is_tls_conn(this.connection)) { if (!supports_starttls) { throw new Error("STARTTLS is not supported by the server"); } @@ -119,7 +119,7 @@ export class SmtpClient { } } - isTlsConn(arg: Deno.Conn): arg is Deno.TlsConn { + is_tls_conn(arg: Deno.Conn): arg is Deno.TlsConn { return (arg as Deno.TlsConn).handshake !== undefined; }