From 918950f5844b0e69138a93b5d09a4086708578ed Mon Sep 17 00:00:00 2001 From: David Krause Date: Sun, 31 Oct 2021 12:53:24 +0100 Subject: [PATCH 1/6] Added support for sending "ehlo" and receiving multiline "ehlo" response. Signed-off-by: David Krause --- lib/pure/smtp.nim | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 5ba048608469a..f459b97ff1351 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -62,6 +62,7 @@ type sock: SocketType address: string debug: bool + useEhlo: bool Smtp* = SmtpBase[Socket] AsyncSmtp* = SmtpBase[AsyncSocket] @@ -100,7 +101,6 @@ proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[string] {.multisync.} = ## `SMTP extensions`_. ## ## See `checkReply(reply)<#checkReply,AsyncSmtp,string>`_. - result = await smtp.sock.recvLine() if smtp.debug: echo("S:" & result) @@ -172,11 +172,13 @@ proc `$`*(msg: Message): string = result.add(msg.msgBody) proc newSmtp*(useSsl = false, debug = false, - sslContext: SslContext = nil): Smtp = + sslContext: SslContext = nil, useEhlo = false): Smtp = ## Creates a new `Smtp` instance. + ## if useEhlo is true, `ehlo` is send instead of `helo` new result result.debug = debug result.sock = newSocket() + result.useEhlo = useEhlo if useSsl: when compiledWithSsl: if sslContext == nil: @@ -187,11 +189,11 @@ proc newSmtp*(useSsl = false, debug = false, {.error: "SMTP module compiled without SSL support".} proc newAsyncSmtp*(useSsl = false, debug = false, - sslContext: SslContext = nil): AsyncSmtp = + sslContext: SslContext = nil, useEhlo = false): AsyncSmtp = ## Creates a new `AsyncSmtp` instance. new result result.debug = debug - + result.useEhlo = useEhlo result.sock = newAsyncSocket() if useSsl: when compiledWithSsl: @@ -220,7 +222,6 @@ proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = ## would need to call when using this module. One exception to ## this is if you are implementing any ## `SMTP extensions`_. - var line = await smtp.debugRecv() if not line.startsWith(reply): await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) @@ -230,6 +231,22 @@ proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = await smtp.debugSend("HELO " & smtp.address & "\c\L") await smtp.checkReply("250") +proc recvEhlo*(smtp: Smtp | AsyncSmtp) {.multisync.} = + ## skips "250-" lines, read until "250 " found + while true: + var line = await smtp.sock.recvLine() + if smtp.debug: + echo("S:" & line) + if line.startsWith("250-"): continue + elif line.startsWith("250 "): break # last line + else: + await quitExcpt(smtp, "Expected 250 reply from EHLO response, got: " & line) + +proc ehlo*(smtp: Smtp | AsyncSmtp) {.multisync.} = + ## Sends EHLO request + await smtp.debugSend("EHLO " & smtp.address & "\c\L") + await smtp.recvEhlo() + proc connect*(smtp: Smtp | AsyncSmtp, address: string, port: Port) {.multisync.} = ## Establishes a connection with a SMTP server. @@ -237,7 +254,10 @@ proc connect*(smtp: Smtp | AsyncSmtp, smtp.address = address await smtp.sock.connect(address, port) await smtp.checkReply("220") - await smtp.helo() + if smtp.useEhlo: + await smtp.ehlo() + else: + await smtp.helo() proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} = ## Put the SMTP connection in TLS (Transport Layer Security) mode. @@ -249,7 +269,10 @@ proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient) else: sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient) - await smtp.helo() + if smtp.useEhlo: + await smtp.ehlo() + else: + await smtp.helo() else: {.error: "SMTP module compiled without SSL support".} From dc6bc634759452b25e08aed346ca5b77b1c03900 Mon Sep 17 00:00:00 2001 From: David Krause Date: Sun, 31 Oct 2021 15:05:07 +0100 Subject: [PATCH 2/6] fix typo Signed-off-by: David Krause --- lib/pure/smtp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index f459b97ff1351..12a6d79e050e8 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -174,7 +174,7 @@ proc `$`*(msg: Message): string = proc newSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil, useEhlo = false): Smtp = ## Creates a new `Smtp` instance. - ## if useEhlo is true, `ehlo` is send instead of `helo` + ## If `useEhlo` is true, `ehlo` is sent instead of `helo` new result result.debug = debug result.sock = newSocket() From 7a40c2a8a4e12642bc88e13ff8db7adfccbd75e1 Mon Sep 17 00:00:00 2001 From: David Krause Date: Sun, 31 Oct 2021 19:03:12 +0100 Subject: [PATCH 3/6] send "EHLO" first, if not supported, send "HELO" to smtp server. Signed-off-by: David Krause --- lib/pure/smtp.nim | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 12a6d79e050e8..a9b7c7e5c58d4 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -62,7 +62,6 @@ type sock: SocketType address: string debug: bool - useEhlo: bool Smtp* = SmtpBase[Socket] AsyncSmtp* = SmtpBase[AsyncSocket] @@ -171,14 +170,11 @@ proc `$`*(msg: Message): string = result.add("\c\L") result.add(msg.msgBody) -proc newSmtp*(useSsl = false, debug = false, - sslContext: SslContext = nil, useEhlo = false): Smtp = +proc newSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): Smtp = ## Creates a new `Smtp` instance. - ## If `useEhlo` is true, `ehlo` is sent instead of `helo` new result result.debug = debug result.sock = newSocket() - result.useEhlo = useEhlo if useSsl: when compiledWithSsl: if sslContext == nil: @@ -188,12 +184,10 @@ proc newSmtp*(useSsl = false, debug = false, else: {.error: "SMTP module compiled without SSL support".} -proc newAsyncSmtp*(useSsl = false, debug = false, - sslContext: SslContext = nil, useEhlo = false): AsyncSmtp = +proc newAsyncSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): AsyncSmtp = ## Creates a new `AsyncSmtp` instance. new result result.debug = debug - result.useEhlo = useEhlo result.sock = newAsyncSocket() if useSsl: when compiledWithSsl: @@ -231,21 +225,21 @@ proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = await smtp.debugSend("HELO " & smtp.address & "\c\L") await smtp.checkReply("250") -proc recvEhlo*(smtp: Smtp | AsyncSmtp) {.multisync.} = +proc recvEhlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = ## skips "250-" lines, read until "250 " found + ## return `true` if server supports `EHLO`, false otherwise while true: var line = await smtp.sock.recvLine() if smtp.debug: echo("S:" & line) if line.startsWith("250-"): continue - elif line.startsWith("250 "): break # last line - else: - await quitExcpt(smtp, "Expected 250 reply from EHLO response, got: " & line) + elif line.startsWith("250 "): return true # last line + else: return false -proc ehlo*(smtp: Smtp | AsyncSmtp) {.multisync.} = +proc ehlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = ## Sends EHLO request await smtp.debugSend("EHLO " & smtp.address & "\c\L") - await smtp.recvEhlo() + return await smtp.recvEhlo() proc connect*(smtp: Smtp | AsyncSmtp, address: string, port: Port) {.multisync.} = @@ -254,9 +248,8 @@ proc connect*(smtp: Smtp | AsyncSmtp, smtp.address = address await smtp.sock.connect(address, port) await smtp.checkReply("220") - if smtp.useEhlo: - await smtp.ehlo() - else: + let speaksEsmtp = await smtp.ehlo() + if not speaksEsmtp: await smtp.helo() proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} = @@ -269,9 +262,8 @@ proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient) else: sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient) - if smtp.useEhlo: - await smtp.ehlo() - else: + let speaksEsmtp = await smtp.ehlo() + if not speaksEsmtp: await smtp.helo() else: {.error: "SMTP module compiled without SSL support".} From 7e4effd94500fa0ea6af6834e8522a266912e2bf Mon Sep 17 00:00:00 2001 From: David Krause Date: Tue, 2 Nov 2021 17:44:15 +0100 Subject: [PATCH 4/6] fix english Signed-off-by: David Krause --- lib/pure/smtp.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index a9b7c7e5c58d4..efa5ae593becf 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -226,8 +226,8 @@ proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = await smtp.checkReply("250") proc recvEhlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = - ## skips "250-" lines, read until "250 " found - ## return `true` if server supports `EHLO`, false otherwise + ## Skips "250-" lines, read until "250 " found. + ## Return `true` if server supports `EHLO`, false otherwise. while true: var line = await smtp.sock.recvLine() if smtp.debug: @@ -237,7 +237,7 @@ proc recvEhlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = else: return false proc ehlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = - ## Sends EHLO request + ## Sends EHLO request. await smtp.debugSend("EHLO " & smtp.address & "\c\L") return await smtp.recvEhlo() From d46cc9ff40ee4cd31e7540003fd4bfc800397563 Mon Sep 17 00:00:00 2001 From: David Krause Date: Tue, 2 Nov 2021 17:53:20 +0100 Subject: [PATCH 5/6] add changelog entry for smtp `ehlo` Signed-off-by: David Krause --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index fbda7058288b2..1636d125ff1fc 100644 --- a/changelog.md +++ b/changelog.md @@ -7,7 +7,9 @@ ## Standard library additions and changes +## `std/smtp` +- Sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. ## Language changes From b4b997b2432817feb06cc2ef3fddc40ff2274ac8 Mon Sep 17 00:00:00 2001 From: David Krause Date: Tue, 2 Nov 2021 20:53:19 +0100 Subject: [PATCH 6/6] recvEhlo must not be exported Signed-off-by: David Krause --- lib/pure/smtp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index efa5ae593becf..f5196ce1e96f2 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -225,7 +225,7 @@ proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = await smtp.debugSend("HELO " & smtp.address & "\c\L") await smtp.checkReply("250") -proc recvEhlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = +proc recvEhlo(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = ## Skips "250-" lines, read until "250 " found. ## Return `true` if server supports `EHLO`, false otherwise. while true: