mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-31 18:32:11 +00:00
FTP client is now able to connect to server over TLS by set `useTls =… (#17219)
* FTP client is now able to connect to server over TLS by set `useTls = true` in newAsyncFtpClient proc
* Update asyncftpclient.nim
* fix CI
* shouldn't use {.error.}
* Update lib/pure/asyncftpclient.nim
Co-authored-by: flywind <xzsflywind@gmail.com>
Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>
This commit is contained in:
@@ -79,7 +79,17 @@
|
||||
|
||||
|
||||
import asyncdispatch, asyncnet, nativesockets, strutils, parseutils, os, times
|
||||
from net import BufferSize
|
||||
from net import BufferSize, SslContext
|
||||
|
||||
when defined(ssl):
|
||||
from net import SslHandshakeType, newContext, SslCVerifyMode
|
||||
var defaultSslContext {.threadvar.}: SslContext
|
||||
|
||||
proc getSSLContext(): SslContext =
|
||||
if defaultSSLContext == nil:
|
||||
defaultSSLContext = newContext(verifyMode = CVerifyPeer)
|
||||
result = defaultSSLContext
|
||||
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -95,6 +105,9 @@ type
|
||||
jobInProgress*: bool
|
||||
job*: FtpJob
|
||||
dsockConnected*: bool
|
||||
useTls: bool
|
||||
when defined(ssl):
|
||||
sslContext: SslContext
|
||||
|
||||
FtpJobType* = enum
|
||||
JRetrText, JRetr, JStore
|
||||
@@ -179,9 +192,20 @@ proc pasv(ftp: AsyncFtpClient) {.async.} =
|
||||
var ip = nums[0 .. ^3]
|
||||
var port = nums[^2 .. ^1]
|
||||
var properPort = port[0].parseInt()*256+port[1].parseInt()
|
||||
await ftp.dsock.connect(ip.join("."), Port(properPort))
|
||||
let address = ip.join(".")
|
||||
await ftp.dsock.connect(address, Port(properPort))
|
||||
ftp.dsockConnected = true
|
||||
|
||||
if ftp.useTls:
|
||||
when defined(ssl):
|
||||
try:
|
||||
ftp.sslContext.wrapConnectedSocket(ftp.dsock, handshakeAsClient, address)
|
||||
except:
|
||||
ftp.dsock.close()
|
||||
raise getCurrentException()
|
||||
else:
|
||||
doAssert false, "TLS support is not available. Cannot connect over TLS. Compile with -d:ssl to enable."
|
||||
|
||||
proc normalizePathSep(path: string): string =
|
||||
return replace(path, '\\', '/')
|
||||
|
||||
@@ -198,12 +222,28 @@ proc connect*(ftp: AsyncFtpClient) {.async.} =
|
||||
# Handle 220 messages from the server
|
||||
assertReply(reply, "220")
|
||||
|
||||
if ftp.useTls:
|
||||
when defined(ssl):
|
||||
assertReply(await(ftp.send("AUTH TLS")), "234")
|
||||
try:
|
||||
ftp.sslContext.wrapConnectedSocket(ftp.csock, handshakeAsClient, ftp.address)
|
||||
except:
|
||||
ftp.csock.close()
|
||||
raise getCurrentException()
|
||||
else:
|
||||
doAssert false, "TLS support is not available. Cannot connect over TLS. Compile with -d:ssl to enable."
|
||||
|
||||
if ftp.user != "":
|
||||
assertReply(await(ftp.send("USER " & ftp.user)), "230", "331")
|
||||
|
||||
if ftp.pass != "":
|
||||
assertReply(await(ftp.send("PASS " & ftp.pass)), "230")
|
||||
|
||||
if ftp.useTls:
|
||||
assertReply(await(ftp.send("PBSZ 0")), "200")
|
||||
assertReply(await(ftp.send("PROT P")), "200")
|
||||
assertReply(await(ftp.send("TYPE I")), "200")
|
||||
|
||||
proc pwd*(ftp: AsyncFtpClient): Future[string] {.async.} =
|
||||
## Returns the current working directory.
|
||||
let wd = await ftp.send("PWD")
|
||||
@@ -220,15 +260,15 @@ proc cdup*(ftp: AsyncFtpClient) {.async.} =
|
||||
|
||||
proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} =
|
||||
## Downloads text data in ASCII mode
|
||||
result = ""
|
||||
assert ftp.dsockConnected
|
||||
while ftp.dsockConnected:
|
||||
let r = await ftp.dsock.recvLine()
|
||||
if r == "":
|
||||
if r.len == 0:
|
||||
ftp.dsock.close()
|
||||
ftp.dsockConnected = false
|
||||
else:
|
||||
result.add(r & "\n")
|
||||
|
||||
if result.len > 0: result.add "\n"
|
||||
result.add r
|
||||
assertReply(await(ftp.expectReply()), "226")
|
||||
|
||||
proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
|
||||
@@ -322,13 +362,14 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt,
|
||||
|
||||
if dataFut.finished:
|
||||
let data = dataFut.read
|
||||
if data != "":
|
||||
if data.len > 0:
|
||||
progress.inc(data.len)
|
||||
progressInSecond.inc(data.len)
|
||||
file.write(data)
|
||||
dataFut = ftp.dsock.recv(BufferSize)
|
||||
else:
|
||||
ftp.dsockConnected = false
|
||||
ftp.dsock.close()
|
||||
|
||||
assertReply(await(ftp.expectReply()), "226")
|
||||
|
||||
@@ -371,7 +412,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File,
|
||||
while ftp.dsockConnected:
|
||||
if sendFut == nil or sendFut.finished:
|
||||
# TODO: Async file reading.
|
||||
let len = file.readBuffer(addr(data[0]), 4000)
|
||||
let len = file.readBuffer(addr data[0], 4000)
|
||||
setLen(data, len)
|
||||
if len == 0:
|
||||
# File finished uploading.
|
||||
@@ -422,7 +463,7 @@ proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} =
|
||||
assertReply(await ftp.send("RMD " & dir), "250")
|
||||
|
||||
proc newAsyncFtpClient*(address: string, port = Port(21),
|
||||
user, pass = "", progressInterval: int = 1000): AsyncFtpClient =
|
||||
user, pass = "", progressInterval: int = 1000, useTls = false, sslContext: SslContext = nil): AsyncFtpClient =
|
||||
## Creates a new `AsyncFtpClient` object.
|
||||
new result
|
||||
result.user = user
|
||||
@@ -432,8 +473,17 @@ proc newAsyncFtpClient*(address: string, port = Port(21),
|
||||
result.progressInterval = progressInterval
|
||||
result.dsockConnected = false
|
||||
result.csock = newAsyncSocket()
|
||||
if useTls:
|
||||
when defined(ssl):
|
||||
result.useTls = true
|
||||
if sslContext == nil:
|
||||
result.sslContext = getSSLContext()
|
||||
else:
|
||||
result.sslContext = sslContext
|
||||
else:
|
||||
doAssert false, "TLS support is not available. Cannot connect over TLS. Compile with -d:ssl to enable."
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
when not defined(testing) and defined(ssl) and isMainModule:
|
||||
var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
|
||||
proc main(ftp: AsyncFtpClient) {.async.} =
|
||||
await ftp.connect()
|
||||
@@ -448,4 +498,19 @@ when not defined(testing) and isMainModule:
|
||||
await ftp.removeDir("deleteme")
|
||||
echo("Finished")
|
||||
|
||||
var ftps = newAsyncFtpClient("example.com", user = "test", pass = "test", useTls = true)
|
||||
proc main1(ftp: AsyncFtpClient) {.async.} =
|
||||
await ftps.connect()
|
||||
echo await ftps.pwd()
|
||||
echo await ftps.listDirs()
|
||||
await ftps.store("payload.jpg", "payload.jpg")
|
||||
await ftps.retrFile("payload.jpg", "payload2.jpg")
|
||||
await ftps.rename("payload.jpg", "payload_renamed.jpg")
|
||||
await ftps.store("payload.jpg", "payload_remove.jpg")
|
||||
await ftps.removeFile("payload_remove.jpg")
|
||||
await ftps.createDir("deleteme")
|
||||
await ftps.removeDir("deleteme")
|
||||
echo("Finished")
|
||||
|
||||
waitFor main(ftp)
|
||||
waitFor main1(ftp)
|
||||
|
||||
Reference in New Issue
Block a user