Skip to content

Commit

Permalink
Added support for sending "ehlo" and receiving multiline "ehlo" respo…
Browse files Browse the repository at this point in the history
…nse. (nim-lang#19077)

* Added support for sending "ehlo"
and receiving multiline "ehlo" response.

Signed-off-by: David Krause <[email protected]>

* fix typo

Signed-off-by: David Krause <[email protected]>

* send "EHLO" first, if not supported, send "HELO" to smtp server.

Signed-off-by: David Krause <[email protected]>

* fix english

Signed-off-by: David Krause <[email protected]>

* add changelog entry for smtp `ehlo`

Signed-off-by: David Krause <[email protected]>

* recvEhlo must not be exported

Signed-off-by: David Krause <[email protected]>
  • Loading branch information
enthus1ast authored and PMunch committed Mar 28, 2022
1 parent 3ce926b commit f9edb60
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 9 deletions.
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

0 comments on commit f9edb60

Please sign in to comment.