Ftpclient module now uses generics.
This commit is contained in:
parent
2dff5ef718
commit
2525215610
1 changed files with 89 additions and 93 deletions
|
|
@ -34,15 +34,11 @@ import sockets, strutils, parseutils, times, os, asyncio
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TFTPClient* = object of TObject
|
PFtpBase*[SockType] = ref TFtpBase[SockType]
|
||||||
case isAsync: bool
|
TFtpBase*[SockType] = object
|
||||||
of false:
|
csock: SockType
|
||||||
csock: TSocket # Command connection socket
|
dsock: SockType
|
||||||
dsock: TSocket # Data connection socket
|
when SockType is PAsyncSocket:
|
||||||
else:
|
|
||||||
dummyA, dummyB: pointer # workaround a Nimrod API issue
|
|
||||||
asyncCSock: PAsyncSocket
|
|
||||||
asyncDSock: PAsyncSocket
|
|
||||||
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
|
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
|
||||||
disp: PDispatcher
|
disp: PDispatcher
|
||||||
asyncDSockID: PDelegate
|
asyncDSockID: PDelegate
|
||||||
|
|
@ -51,17 +47,16 @@ type
|
||||||
port: TPort
|
port: TPort
|
||||||
|
|
||||||
jobInProgress: bool
|
jobInProgress: bool
|
||||||
job: ref TFTPJob
|
job: PFTPJob[SockType]
|
||||||
|
|
||||||
dsockConnected: bool
|
dsockConnected: bool
|
||||||
|
|
||||||
PFTPClient* = ref TFTPClient
|
|
||||||
|
|
||||||
FTPJobType* = enum
|
FTPJobType* = enum
|
||||||
JRetrText, JRetr, JStore
|
JRetrText, JRetr, JStore
|
||||||
|
|
||||||
TFTPJob = object
|
PFtpJob[T] = ref TFtpJob[T]
|
||||||
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
|
TFTPJob[T] = object
|
||||||
|
prc: proc (ftp: PFTPBase[T], async: bool): bool {.nimcall, gcsafe.}
|
||||||
case typ*: FTPJobType
|
case typ*: FTPJobType
|
||||||
of JRetrText:
|
of JRetrText:
|
||||||
lines: string
|
lines: string
|
||||||
|
|
@ -75,8 +70,11 @@ type
|
||||||
toStore: string # Data left to upload (Only used with async)
|
toStore: string # Data left to upload (Only used with async)
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
|
TFtpClient* = TFtpBase[TSocket]
|
||||||
|
PFtpClient* = ref TFTPClient
|
||||||
|
|
||||||
PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient.
|
PAsyncFTPClient* = ref TAsyncFTPClient ## Async alternative to TFTPClient.
|
||||||
TAsyncFTPClient* = object of TFTPClient
|
TAsyncFTPClient* = TFtpBase[PAsyncSocket]
|
||||||
|
|
||||||
FTPEventType* = enum
|
FTPEventType* = enum
|
||||||
EvTransferProgress, EvLines, EvRetr, EvStore
|
EvTransferProgress, EvLines, EvRetr, EvStore
|
||||||
|
|
@ -106,30 +104,30 @@ proc ftpClient*(address: string, port = TPort(21),
|
||||||
result.address = address
|
result.address = address
|
||||||
result.port = port
|
result.port = port
|
||||||
|
|
||||||
result.isAsync = false
|
|
||||||
result.dsockConnected = false
|
result.dsockConnected = false
|
||||||
result.csock = socket()
|
result.csock = socket()
|
||||||
if result.csock == InvalidSocket: osError(osLastError())
|
if result.csock == InvalidSocket: osError(osLastError())
|
||||||
|
|
||||||
proc getDSock(ftp: PFTPClient): TSocket =
|
proc getDSock[T](ftp: PFTPBase[T]): TSocket =
|
||||||
if ftp.isAsync: return ftp.asyncDSock else: return ftp.dsock
|
return ftp.dsock
|
||||||
|
|
||||||
proc getCSock(ftp: PFTPClient): TSocket =
|
proc getCSock[T](ftp: PFTPBase[T]): TSocket =
|
||||||
if ftp.isAsync: return ftp.asyncCSock else: return ftp.csock
|
return ftp.csock
|
||||||
|
|
||||||
template blockingOperation(sock: TSocket, body: stmt) {.immediate.} =
|
template blockingOperation(sock: TSocket, body: stmt) {.immediate.} =
|
||||||
if ftp.isAsync:
|
body
|
||||||
|
|
||||||
|
template blockingOperation(sock: PAsyncSocket, body: stmt) {.immediate.} =
|
||||||
sock.setBlocking(true)
|
sock.setBlocking(true)
|
||||||
body
|
body
|
||||||
if ftp.isAsync:
|
|
||||||
sock.setBlocking(false)
|
sock.setBlocking(false)
|
||||||
|
|
||||||
proc expectReply(ftp: PFTPClient): TaintedString =
|
proc expectReply[T](ftp: PFtpBase[T]): TaintedString =
|
||||||
result = TaintedString""
|
result = TaintedString""
|
||||||
blockingOperation(ftp.getCSock()):
|
blockingOperation(ftp.getCSock()):
|
||||||
ftp.getCSock().readLine(result)
|
ftp.getCSock().readLine(result)
|
||||||
|
|
||||||
proc send*(ftp: PFTPClient, m: string): TaintedString =
|
proc send*[T](ftp: PFtpBase[T], m: string): TaintedString =
|
||||||
## Send a message to the server, and wait for a primary reply.
|
## Send a message to the server, and wait for a primary reply.
|
||||||
## ``\c\L`` is added for you.
|
## ``\c\L`` is added for you.
|
||||||
blockingOperation(ftp.getCSock()):
|
blockingOperation(ftp.getCSock()):
|
||||||
|
|
@ -149,8 +147,8 @@ proc assertReply(received: TaintedString, expected: varargs[string]) =
|
||||||
"Expected reply '$1' got: $2" %
|
"Expected reply '$1' got: $2" %
|
||||||
[expected.join("' or '"), received.string])
|
[expected.join("' or '"), received.string])
|
||||||
|
|
||||||
proc createJob(ftp: PFTPClient,
|
proc createJob[T](ftp: PFtpBase[T],
|
||||||
prc: proc (ftp: PFTPClient, async: bool): bool {.
|
prc: proc (ftp: PFtpBase[T], async: bool): bool {.
|
||||||
nimcall,gcsafe.},
|
nimcall,gcsafe.},
|
||||||
cmd: FTPJobType) =
|
cmd: FTPJobType) =
|
||||||
if ftp.jobInProgress:
|
if ftp.jobInProgress:
|
||||||
|
|
@ -165,7 +163,7 @@ proc createJob(ftp: PFTPClient,
|
||||||
of JRetr, JStore:
|
of JRetr, JStore:
|
||||||
ftp.job.toStore = ""
|
ftp.job.toStore = ""
|
||||||
|
|
||||||
proc deleteJob(ftp: PFTPClient) =
|
proc deleteJob[T](ftp: PFtpBase[T]) =
|
||||||
assert ftp.jobInProgress
|
assert ftp.jobInProgress
|
||||||
ftp.jobInProgress = false
|
ftp.jobInProgress = false
|
||||||
case ftp.job.typ
|
case ftp.job.typ
|
||||||
|
|
@ -173,12 +171,9 @@ proc deleteJob(ftp: PFTPClient) =
|
||||||
ftp.job.lines = ""
|
ftp.job.lines = ""
|
||||||
of JRetr, JStore:
|
of JRetr, JStore:
|
||||||
ftp.job.file.close()
|
ftp.job.file.close()
|
||||||
if ftp.isAsync:
|
|
||||||
ftp.asyncDSock.close()
|
|
||||||
else:
|
|
||||||
ftp.dsock.close()
|
ftp.dsock.close()
|
||||||
|
|
||||||
proc handleTask(s: PAsyncSocket, ftp: PFTPClient) =
|
proc handleTask(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
||||||
if ftp.jobInProgress:
|
if ftp.jobInProgress:
|
||||||
if ftp.job.typ in {JRetr, JStore}:
|
if ftp.job.typ in {JRetr, JStore}:
|
||||||
if epochTime() - ftp.job.lastProgressReport >= 1.0:
|
if epochTime() - ftp.job.lastProgressReport >= 1.0:
|
||||||
|
|
@ -193,12 +188,12 @@ proc handleTask(s: PAsyncSocket, ftp: PFTPClient) =
|
||||||
ftp.job.oneSecond = 0
|
ftp.job.oneSecond = 0
|
||||||
ftp.handleEvent(PAsyncFTPClient(ftp), r)
|
ftp.handleEvent(PAsyncFTPClient(ftp), r)
|
||||||
|
|
||||||
proc handleWrite(s: PAsyncSocket, ftp: PFTPClient) =
|
proc handleWrite(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
||||||
if ftp.jobInProgress:
|
if ftp.jobInProgress:
|
||||||
if ftp.job.typ == JStore:
|
if ftp.job.typ == JStore:
|
||||||
assert (not ftp.job.prc(ftp, true))
|
assert (not ftp.job.prc(ftp, true))
|
||||||
|
|
||||||
proc handleConnect(s: PAsyncSocket, ftp: PFTPClient) =
|
proc handleConnect(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
||||||
ftp.dsockConnected = true
|
ftp.dsockConnected = true
|
||||||
assert(ftp.jobInProgress)
|
assert(ftp.jobInProgress)
|
||||||
if ftp.job.typ == JStore:
|
if ftp.job.typ == JStore:
|
||||||
|
|
@ -206,30 +201,32 @@ proc handleConnect(s: PAsyncSocket, ftp: PFTPClient) =
|
||||||
else:
|
else:
|
||||||
s.delHandleWrite()
|
s.delHandleWrite()
|
||||||
|
|
||||||
proc handleRead(s: PAsyncSocket, ftp: PFTPClient) =
|
proc handleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
||||||
assert ftp.jobInProgress
|
assert ftp.jobInProgress
|
||||||
assert ftp.job.typ != JStore
|
assert ftp.job.typ != JStore
|
||||||
# This can never return true, because it shouldn't check for code
|
# This can never return true, because it shouldn't check for code
|
||||||
# 226 from csock.
|
# 226 from csock.
|
||||||
assert(not ftp.job.prc(ftp, true))
|
assert(not ftp.job.prc(ftp, true))
|
||||||
|
|
||||||
proc pasv(ftp: PFTPClient) =
|
proc pasv[T](ftp: PFtpBase[T]) =
|
||||||
## Negotiate a data connection.
|
## Negotiate a data connection.
|
||||||
if not ftp.isAsync:
|
when T is TSocket:
|
||||||
ftp.dsock = socket()
|
ftp.dsock = socket()
|
||||||
if ftp.dsock == InvalidSocket: osError(osLastError())
|
if ftp.dsock == InvalidSocket: osError(osLastError())
|
||||||
else:
|
elif T is PAsyncSocket:
|
||||||
ftp.asyncDSock = AsyncSocket()
|
ftp.dsock = AsyncSocket()
|
||||||
ftp.asyncDSock.handleRead =
|
ftp.dsock.handleRead =
|
||||||
proc (s: PAsyncSocket) =
|
proc (s: PAsyncSocket) =
|
||||||
handleRead(s, ftp)
|
handleRead(s, ftp)
|
||||||
ftp.asyncDSock.handleConnect =
|
ftp.dsock.handleConnect =
|
||||||
proc (s: PAsyncSocket) =
|
proc (s: PAsyncSocket) =
|
||||||
handleConnect(s, ftp)
|
handleConnect(s, ftp)
|
||||||
ftp.asyncDSock.handleTask =
|
ftp.dsock.handleTask =
|
||||||
proc (s: PAsyncSocket) =
|
proc (s: PAsyncSocket) =
|
||||||
handleTask(s, ftp)
|
handleTask(s, ftp)
|
||||||
ftp.disp.register(ftp.asyncDSock)
|
ftp.disp.register(ftp.dsock)
|
||||||
|
else:
|
||||||
|
{.fatal: "Incorrect socket instantiation".}
|
||||||
|
|
||||||
var pasvMsg = ftp.send("PASV").string.strip.TaintedString
|
var pasvMsg = ftp.send("PASV").string.strip.TaintedString
|
||||||
assertReply(pasvMsg, "227")
|
assertReply(pasvMsg, "227")
|
||||||
|
|
@ -238,23 +235,24 @@ proc pasv(ftp: PFTPClient) =
|
||||||
var ip = nums[0.. -3]
|
var ip = nums[0.. -3]
|
||||||
var port = nums[-2.. -1]
|
var port = nums[-2.. -1]
|
||||||
var properPort = port[0].parseInt()*256+port[1].parseInt()
|
var properPort = port[0].parseInt()*256+port[1].parseInt()
|
||||||
if ftp.isAsync:
|
ftp.dsock.connect(ip.join("."), TPort(properPort.toU16))
|
||||||
ftp.asyncDSock.connect(ip.join("."), TPort(properPort.toU16))
|
when T is PAsyncSocket:
|
||||||
ftp.dsockConnected = False
|
ftp.dsockConnected = False
|
||||||
else:
|
else:
|
||||||
ftp.dsock.connect(ip.join("."), TPort(properPort.toU16))
|
|
||||||
ftp.dsockConnected = True
|
ftp.dsockConnected = True
|
||||||
|
|
||||||
proc normalizePathSep(path: string): string =
|
proc normalizePathSep(path: string): string =
|
||||||
return replace(path, '\\', '/')
|
return replace(path, '\\', '/')
|
||||||
|
|
||||||
proc connect*(ftp: PFTPClient) =
|
proc connect*[T](ftp: PFtpBase[T]) =
|
||||||
## Connect to the FTP server specified by ``ftp``.
|
## Connect to the FTP server specified by ``ftp``.
|
||||||
if ftp.isAsync:
|
when T is PAsyncSocket:
|
||||||
blockingOperation(ftp.asyncCSock):
|
blockingOperation(ftp.csock):
|
||||||
ftp.asyncCSock.connect(ftp.address, ftp.port)
|
|
||||||
else:
|
|
||||||
ftp.csock.connect(ftp.address, ftp.port)
|
ftp.csock.connect(ftp.address, ftp.port)
|
||||||
|
elif T is TSocket:
|
||||||
|
ftp.csock.connect(ftp.address, ftp.port)
|
||||||
|
else:
|
||||||
|
{.fatal: "Incorrect socket instantiation".}
|
||||||
|
|
||||||
# TODO: Handle 120? or let user handle it.
|
# TODO: Handle 120? or let user handle it.
|
||||||
assertReply ftp.expectReply(), "220"
|
assertReply ftp.expectReply(), "220"
|
||||||
|
|
@ -279,25 +277,27 @@ proc cdup*(ftp: PFTPClient) =
|
||||||
## Changes the current directory to the parent of the current directory.
|
## Changes the current directory to the parent of the current directory.
|
||||||
assertReply ftp.send("CDUP"), "200"
|
assertReply ftp.send("CDUP"), "200"
|
||||||
|
|
||||||
proc getLines(ftp: PFTPClient, async: bool = false): bool =
|
proc getLines[T](ftp: PFtpBase[T], async: bool = false): bool =
|
||||||
## Downloads text data in ASCII mode
|
## Downloads text data in ASCII mode
|
||||||
## Returns true if the download is complete.
|
## Returns true if the download is complete.
|
||||||
## It doesn't if `async` is true, because it doesn't check for 226 then.
|
## It doesn't if `async` is true, because it doesn't check for 226 then.
|
||||||
if ftp.dsockConnected:
|
if ftp.dsockConnected:
|
||||||
var r = TaintedString""
|
var r = TaintedString""
|
||||||
if ftp.isAsync:
|
when T is PAsyncSocket:
|
||||||
if ftp.asyncDSock.readLine(r):
|
if ftp.asyncDSock.readLine(r):
|
||||||
if r.string == "":
|
if r.string == "":
|
||||||
ftp.dsockConnected = false
|
ftp.dsockConnected = false
|
||||||
else:
|
else:
|
||||||
ftp.job.lines.add(r.string & "\n")
|
ftp.job.lines.add(r.string & "\n")
|
||||||
else:
|
elif T is TSocket:
|
||||||
assert(not async)
|
assert(not async)
|
||||||
ftp.dsock.readLine(r)
|
ftp.dsock.readLine(r)
|
||||||
if r.string == "":
|
if r.string == "":
|
||||||
ftp.dsockConnected = false
|
ftp.dsockConnected = false
|
||||||
else:
|
else:
|
||||||
ftp.job.lines.add(r.string & "\n")
|
ftp.job.lines.add(r.string & "\n")
|
||||||
|
else:
|
||||||
|
{.fatal: "Incorrect socket instantiation".}
|
||||||
|
|
||||||
if not async:
|
if not async:
|
||||||
var readSocks: seq[TSocket] = @[ftp.getCSock()]
|
var readSocks: seq[TSocket] = @[ftp.getCSock()]
|
||||||
|
|
@ -307,14 +307,14 @@ proc getLines(ftp: PFTPClient, async: bool = false): bool =
|
||||||
assertReply ftp.expectReply(), "226"
|
assertReply ftp.expectReply(), "226"
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc listDirs*(ftp: PFTPClient, dir: string = "",
|
proc listDirs*[T](ftp: PFtpBase[T], dir: string = "",
|
||||||
async = false): seq[string] =
|
async = false): seq[string] =
|
||||||
## Returns a list of filenames in the given directory. If ``dir`` is "",
|
## Returns a list of filenames in the given directory. If ``dir`` is "",
|
||||||
## the current directory is used. If ``async`` is true, this
|
## the current directory is used. If ``async`` is true, this
|
||||||
## function will return immediately and it will be your job to
|
## function will return immediately and it will be your job to
|
||||||
## use asyncio's ``poll`` to progress this operation.
|
## use asyncio's ``poll`` to progress this operation.
|
||||||
|
|
||||||
ftp.createJob(getLines, JRetrText)
|
ftp.createJob(getLines[T], JRetrText)
|
||||||
ftp.pasv()
|
ftp.pasv()
|
||||||
|
|
||||||
assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"]
|
assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"]
|
||||||
|
|
@ -384,12 +384,12 @@ proc chmod*(ftp: PFTPClient, path: string,
|
||||||
assertReply ftp.send("SITE CHMOD " & perm &
|
assertReply ftp.send("SITE CHMOD " & perm &
|
||||||
" " & path.normalizePathSep), "200"
|
" " & path.normalizePathSep), "200"
|
||||||
|
|
||||||
proc list*(ftp: PFTPClient, dir: string = "", async = false): string =
|
proc list*[T](ftp: PFtpBase[T], dir: string = "", async = false): string =
|
||||||
## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
|
## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
|
||||||
## working directory. If ``async`` is true, this function will return
|
## working directory. If ``async`` is true, this function will return
|
||||||
## immediately and it will be your job to call asyncio's
|
## immediately and it will be your job to call asyncio's
|
||||||
## ``poll`` to progress this operation.
|
## ``poll`` to progress this operation.
|
||||||
ftp.createJob(getLines, JRetrText)
|
ftp.createJob(getLines[T], JRetrText)
|
||||||
ftp.pasv()
|
ftp.pasv()
|
||||||
|
|
||||||
assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"])
|
assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"])
|
||||||
|
|
@ -401,11 +401,11 @@ proc list*(ftp: PFTPClient, dir: string = "", async = false): string =
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
proc retrText*(ftp: PFTPClient, file: string, async = false): string =
|
proc retrText*[T](ftp: PFtpBase[T], file: string, async = false): string =
|
||||||
## Retrieves ``file``. File must be ASCII text.
|
## Retrieves ``file``. File must be ASCII text.
|
||||||
## If ``async`` is true, this function will return immediately and
|
## If ``async`` is true, this function will return immediately and
|
||||||
## it will be your job to call asyncio's ``poll`` to progress this operation.
|
## it will be your job to call asyncio's ``poll`` to progress this operation.
|
||||||
ftp.createJob(getLines, JRetrText)
|
ftp.createJob(getLines[T], JRetrText)
|
||||||
ftp.pasv()
|
ftp.pasv()
|
||||||
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
|
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
|
||||||
|
|
||||||
|
|
@ -416,14 +416,16 @@ proc retrText*(ftp: PFTPClient, file: string, async = false): string =
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
proc getFile(ftp: PFTPClient, async = false): bool =
|
proc getFile[T](ftp: PFtpBase[T], async = false): bool =
|
||||||
if ftp.dsockConnected:
|
if ftp.dsockConnected:
|
||||||
var r = "".TaintedString
|
var r = "".TaintedString
|
||||||
var bytesRead = 0
|
var bytesRead = 0
|
||||||
var returned = false
|
var returned = false
|
||||||
if async:
|
if async:
|
||||||
if not ftp.isAsync: raise newException(EFTP, "FTPClient must be async.")
|
when T is TSocket:
|
||||||
bytesRead = ftp.AsyncDSock.recvAsync(r, BufferSize)
|
raise newException(EFTP, "FTPClient must be async.")
|
||||||
|
else:
|
||||||
|
bytesRead = ftp.dsock.recvAsync(r, BufferSize)
|
||||||
returned = bytesRead != -1
|
returned = bytesRead != -1
|
||||||
else:
|
else:
|
||||||
bytesRead = getDSock(ftp).recv(r, BufferSize)
|
bytesRead = getDSock(ftp).recv(r, BufferSize)
|
||||||
|
|
@ -443,13 +445,13 @@ proc getFile(ftp: PFTPClient, async = false): bool =
|
||||||
assertReply ftp.expectReply(), "226"
|
assertReply ftp.expectReply(), "226"
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) =
|
proc retrFile*[T](ftp: PFtpBase[T], file, dest: string, async = false) =
|
||||||
## Downloads ``file`` and saves it to ``dest``. Usage of this function
|
## Downloads ``file`` and saves it to ``dest``. Usage of this function
|
||||||
## asynchronously is recommended to view the progress of the download.
|
## asynchronously is recommended to view the progress of the download.
|
||||||
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
||||||
## when the download is finished, and the ``filename`` field will be equal
|
## when the download is finished, and the ``filename`` field will be equal
|
||||||
## to ``file``.
|
## to ``file``.
|
||||||
ftp.createJob(getFile, JRetr)
|
ftp.createJob(getFile[T], JRetr)
|
||||||
ftp.job.file = open(dest, mode = fmWrite)
|
ftp.job.file = open(dest, mode = fmWrite)
|
||||||
ftp.pasv()
|
ftp.pasv()
|
||||||
var reply = ftp.send("RETR " & file.normalizePathSep)
|
var reply = ftp.send("RETR " & file.normalizePathSep)
|
||||||
|
|
@ -468,11 +470,11 @@ proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) =
|
||||||
while not ftp.job.prc(ftp, false): discard
|
while not ftp.job.prc(ftp, false): discard
|
||||||
ftp.deleteJob()
|
ftp.deleteJob()
|
||||||
|
|
||||||
proc doUpload(ftp: PFTPClient, async = false): bool =
|
proc doUpload[T](ftp: PFtpBase[T], async = false): bool =
|
||||||
if ftp.dsockConnected:
|
if ftp.dsockConnected:
|
||||||
if ftp.job.toStore.len() > 0:
|
if ftp.job.toStore.len() > 0:
|
||||||
assert(async)
|
assert(async)
|
||||||
let bytesSent = ftp.asyncDSock.sendAsync(ftp.job.toStore)
|
let bytesSent = ftp.dsock.sendAsync(ftp.job.toStore)
|
||||||
if bytesSent == ftp.job.toStore.len:
|
if bytesSent == ftp.job.toStore.len:
|
||||||
ftp.job.toStore = ""
|
ftp.job.toStore = ""
|
||||||
elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
|
elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
|
||||||
|
|
@ -485,7 +487,7 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
|
||||||
setLen(s, len)
|
setLen(s, len)
|
||||||
if len == 0:
|
if len == 0:
|
||||||
# File finished uploading.
|
# File finished uploading.
|
||||||
if ftp.isAsync: ftp.asyncDSock.close() else: ftp.dsock.close()
|
ftp.dsock.close()
|
||||||
ftp.dsockConnected = false
|
ftp.dsockConnected = false
|
||||||
|
|
||||||
if not async:
|
if not async:
|
||||||
|
|
@ -496,7 +498,7 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
|
||||||
if not async:
|
if not async:
|
||||||
getDSock(ftp).send(s)
|
getDSock(ftp).send(s)
|
||||||
else:
|
else:
|
||||||
let bytesSent = ftp.asyncDSock.sendAsync(s)
|
let bytesSent = ftp.dsock.sendAsync(s)
|
||||||
if bytesSent == 0:
|
if bytesSent == 0:
|
||||||
ftp.job.toStore.add(s)
|
ftp.job.toStore.add(s)
|
||||||
elif bytesSent != s.len:
|
elif bytesSent != s.len:
|
||||||
|
|
@ -506,14 +508,14 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
|
||||||
ftp.job.progress.inc(len)
|
ftp.job.progress.inc(len)
|
||||||
ftp.job.oneSecond.inc(len)
|
ftp.job.oneSecond.inc(len)
|
||||||
|
|
||||||
proc store*(ftp: PFTPClient, file, dest: string, async = false) =
|
proc store*[T](ftp: PFtpBase[T], file, dest: string, async = false) =
|
||||||
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
||||||
## function asynchronously is recommended to view the progress of
|
## function asynchronously is recommended to view the progress of
|
||||||
## the download.
|
## the download.
|
||||||
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
|
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
|
||||||
## when the upload is finished, and the ``filename`` field will be
|
## when the upload is finished, and the ``filename`` field will be
|
||||||
## equal to ``file``.
|
## equal to ``file``.
|
||||||
ftp.createJob(doUpload, JStore)
|
ftp.createJob(doUpload[T], JStore)
|
||||||
ftp.job.file = open(file)
|
ftp.job.file = open(file)
|
||||||
ftp.job.total = ftp.job.file.getFileSize()
|
ftp.job.total = ftp.job.file.getFileSize()
|
||||||
ftp.job.lastProgressReport = epochTime()
|
ftp.job.lastProgressReport = epochTime()
|
||||||
|
|
@ -526,14 +528,10 @@ proc store*(ftp: PFTPClient, file, dest: string, async = false) =
|
||||||
while not ftp.job.prc(ftp, false): discard
|
while not ftp.job.prc(ftp, false): discard
|
||||||
ftp.deleteJob()
|
ftp.deleteJob()
|
||||||
|
|
||||||
proc close*(ftp: PFTPClient) =
|
proc close*[T](ftp: PFTPBase[T]) =
|
||||||
## Terminates the connection to the server.
|
## Terminates the connection to the server.
|
||||||
assertReply ftp.send("QUIT"), "221"
|
assertReply ftp.send("QUIT"), "221"
|
||||||
if ftp.jobInProgress: ftp.deleteJob()
|
if ftp.jobInProgress: ftp.deleteJob()
|
||||||
if ftp.isAsync:
|
|
||||||
ftp.asyncCSock.close()
|
|
||||||
ftp.asyncDSock.close()
|
|
||||||
else:
|
|
||||||
ftp.csock.close()
|
ftp.csock.close()
|
||||||
ftp.dsock.close()
|
ftp.dsock.close()
|
||||||
|
|
||||||
|
|
@ -572,20 +570,18 @@ proc asyncFTPClient*(address: string, port = TPort(21),
|
||||||
dres.pass = pass
|
dres.pass = pass
|
||||||
dres.address = address
|
dres.address = address
|
||||||
dres.port = port
|
dres.port = port
|
||||||
dres.isAsync = true
|
|
||||||
dres.dsockConnected = false
|
dres.dsockConnected = false
|
||||||
dres.handleEvent = handleEvent
|
dres.handleEvent = handleEvent
|
||||||
dres.asyncCSock = AsyncSocket()
|
dres.csock = AsyncSocket()
|
||||||
dres.asyncCSock.handleRead =
|
dres.csock.handleRead =
|
||||||
proc (s: PAsyncSocket) =
|
proc (s: PAsyncSocket) =
|
||||||
csockHandleRead(s, dres)
|
csockHandleRead(s, dres)
|
||||||
result = dres
|
result = dres
|
||||||
|
|
||||||
proc register*(d: PDispatcher, ftp: PAsyncFTPClient): PDelegate {.discardable.} =
|
proc register*(d: PDispatcher, ftp: PAsyncFTPClient): PDelegate {.discardable.} =
|
||||||
## Registers ``ftp`` with dispatcher ``d``.
|
## Registers ``ftp`` with dispatcher ``d``.
|
||||||
assert ftp.isAsync
|
|
||||||
ftp.disp = d
|
ftp.disp = d
|
||||||
return ftp.disp.register(ftp.asyncCSock)
|
return ftp.disp.register(ftp.csock)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
proc main =
|
proc main =
|
||||||
|
|
@ -595,7 +591,7 @@ when isMainModule:
|
||||||
case event.typ
|
case event.typ
|
||||||
of EvStore:
|
of EvStore:
|
||||||
echo("Upload finished!")
|
echo("Upload finished!")
|
||||||
ftp.retrFile("payload.JPG", "payload2.JPG", async = true)
|
ftp.retrFile("payload.jpg", "payload2.jpg", async = true)
|
||||||
of EvTransferProgress:
|
of EvTransferProgress:
|
||||||
var time: int64 = -1
|
var time: int64 = -1
|
||||||
if event.speed != 0:
|
if event.speed != 0:
|
||||||
|
|
@ -610,29 +606,29 @@ when isMainModule:
|
||||||
ftp.close()
|
ftp.close()
|
||||||
echo d.len
|
echo d.len
|
||||||
else: assert(false)
|
else: assert(false)
|
||||||
var ftp = asyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev)
|
var ftp = asyncFTPClient("example.com", user = "foo", pass = "bar", handleEvent = hev)
|
||||||
|
|
||||||
d.register(ftp)
|
d.register(ftp)
|
||||||
d.len.echo()
|
d.len.echo()
|
||||||
ftp.connect()
|
ftp.connect()
|
||||||
echo "connected"
|
echo "connected"
|
||||||
ftp.store("payload.JPG", "payload.JPG", async = true)
|
ftp.store("payload.jpg", "payload.jpg", async = true)
|
||||||
d.len.echo()
|
d.len.echo()
|
||||||
echo "uploading..."
|
echo "uploading..."
|
||||||
while true:
|
while true:
|
||||||
if not d.poll(): break
|
if not d.poll(): break
|
||||||
main()
|
main()
|
||||||
|
|
||||||
when isMainModule and false:
|
when not isMainModule:
|
||||||
var ftp = ftpClient("picheta.me", user = "asdasd", pass = "asfwq")
|
var ftp = ftpClient("example.com", user = "foo", pass = "bar")
|
||||||
ftp.connect()
|
ftp.connect()
|
||||||
echo ftp.pwd()
|
echo ftp.pwd()
|
||||||
echo ftp.list()
|
echo ftp.list()
|
||||||
echo("uploading")
|
echo("uploading")
|
||||||
ftp.store("payload.JPG", "payload.JPG", async = false)
|
ftp.store("payload.jpg", "payload.jpg", async = false)
|
||||||
|
|
||||||
echo("Upload complete")
|
echo("Upload complete")
|
||||||
ftp.retrFile("payload.JPG", "payload2.JPG", async = false)
|
ftp.retrFile("payload.jpg", "payload2.jpg", async = false)
|
||||||
|
|
||||||
echo("Download complete")
|
echo("Download complete")
|
||||||
sleep(5000)
|
sleep(5000)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue