Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for sending "ehlo" and receiving multiline "ehlo" response. #19077

Merged
merged 6 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
33 changes: 24 additions & 9 deletions lib/pure/smtp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[string] {.multisync.} =
## `SMTP extensions<https://en.wikipedia.org/wiki/Extended_SMTP>`_.
##
## See `checkReply(reply)<#checkReply,AsyncSmtp,string>`_.

result = await smtp.sock.recvLine()
if smtp.debug:
echo("S:" & result)
Expand Down Expand Up @@ -171,8 +170,7 @@ proc `$`*(msg: Message): string =
result.add("\c\L")
result.add(msg.msgBody)

proc newSmtp*(useSsl = false, debug = false,
sslContext: SslContext = nil): Smtp =
proc newSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): Smtp =
## Creates a new `Smtp` instance.
new result
result.debug = debug
Expand All @@ -186,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): AsyncSmtp =
proc newAsyncSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): AsyncSmtp =
## Creates a new `AsyncSmtp` instance.
new result
result.debug = debug

result.sock = newAsyncSocket()
if useSsl:
when compiledWithSsl:
Expand Down Expand Up @@ -220,7 +216,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<https://en.wikipedia.org/wiki/Extended_SMTP>`_.

var line = await smtp.debugRecv()
if not line.startsWith(reply):
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
Expand All @@ -230,14 +225,32 @@ 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.} =
## 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 "): return true # last line
else: return false

proc ehlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} =
## Sends EHLO request.
await smtp.debugSend("EHLO " & smtp.address & "\c\L")
return await smtp.recvEhlo()

proc connect*(smtp: Smtp | AsyncSmtp,
address: string, port: Port) {.multisync.} =
## Establishes a connection with a SMTP server.
## May fail with ReplyError or with a socket error.
smtp.address = address
await smtp.sock.connect(address, port)
await smtp.checkReply("220")
await smtp.helo()
let speaksEsmtp = await smtp.ehlo()
if not speaksEsmtp:
await smtp.helo()

proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} =
## Put the SMTP connection in TLS (Transport Layer Security) mode.
Expand All @@ -249,7 +262,9 @@ proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync
getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient)
else:
sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient)
await smtp.helo()
let speaksEsmtp = await smtp.ehlo()
if not speaksEsmtp:
await smtp.helo()
else:
{.error: "SMTP module compiled without SSL support".}

Expand Down