diff --git a/Project.toml b/Project.toml index a839051..23a257c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SFTPClient" uuid = "6f0ef2a9-1531-47bc-8c87-f4ce50005508" authors = ["Erik Stensmo "] -version = "0.4.0" +version = "0.4.1" [deps] ArgTools = "0dad84c5-d112-42e6-8d28-ef12dabb789f" diff --git a/src/SFTPClient.jl b/src/SFTPClient.jl index 184e7ab..54f0a25 100644 --- a/src/SFTPClient.jl +++ b/src/SFTPClient.jl @@ -1,6 +1,6 @@ module SFTPClient -export SFTP, readdir, download,upload, cd, rm, rmdir, mkdir, mv, sftpstat, SFTPStatStruct, isdir, isfile, filemode +export SFTP, readdir, download,upload, cd, rm, rmdir, mkdir, mv, sftpstat, SFTPStatStruct, isdir, isfile, filemode, walkdir include("SFTPImpl.jl") diff --git a/src/SFTPImpl.jl b/src/SFTPImpl.jl index ec471ad..366b47e 100644 --- a/src/SFTPImpl.jl +++ b/src/SFTPImpl.jl @@ -111,8 +111,8 @@ function create_fingerprint(hostNameOrIP::AbstractString) end """ -function SFTP(url::AbstractString, username::AbstractString;disable_verify_peer=false, disable_verify_host=false) - +SFTP(url::AbstractString, username::AbstractString, public_key_file::AbstractString, private_key_file::AbstractString;disable_verify_peer=false, disable_verify_host=false, verbose=false) + Creates a new SFTP client using certificate authentication, and keys in the files specified sftp = SFTP("sftp://mysitewhereIhaveACertificate.com", "myuser", "test.pub", "test.pem") @@ -134,7 +134,7 @@ end """ -function SFTP(url::AbstractString, username::AbstractString;disable_verify_peer=false, disable_verify_host=false) + SFTP(url::AbstractString, username::AbstractString;disable_verify_peer=false, disable_verify_host=false) Creates a new SFTP client using certificate authentication. @@ -164,7 +164,7 @@ function SFTP(url::AbstractString, username::AbstractString;disable_verify_peer= end """ -function SFTP(url::AbstractString, username::AbstractString, password::AbstractString;create_known_hosts_entry=true, disable_verify_peer=false, disable_verify_host=false) + SFTP(url::AbstractString, username::AbstractString, password::AbstractString;create_known_hosts_entry=true, disable_verify_peer=false, disable_verify_host=false) Creates a new SFTP Client: url: The url to connect to, e.g., sftp://mysite.com @@ -298,28 +298,90 @@ function ftp_command(sftp::SFTP, command::String) end -function walkdir(sftp::SFTP, dir; topdown=true, follow_symlinks=false, onerror=throw) +function sftpstat2(sftp, dir::String) + + stats = sftpstat(sftp, dir) + a = filter(x->x.desc != "." && x.desc != "..",stats ) + return a; +end + +function myjoinpath(path::AbstractString, name::AbstractString) + path == "." && return name + #println("path $path") + #println("name $name") + path*"/" * name * "/" + +end + + +""" + walkdir(sftp::SFTP, root; topdown=true, follow_symlinks=false, onerror=throw) + Return an iterator that walks the directory tree of a directory. + The iterator returns a tuple containing `(rootpath, dirs, files)`. + + + # Examples + ```julia + for (root, dirs, files) in walkdir(sftp, ".") + println("Directories in \$root") + for dir in dirs + println(joinpath(root, dir)) # path to directories + end + println("Files in \$root") + for file in files + println(joinpath(root, file)) # path to files + end + end + ``` + +""" +function walkdir(sftp::SFTP, root; topdown=true, follow_symlinks=false, onerror=throw) function _walkdir(chnl, root) - tryf(f, p) = try + + tryf2( f, sftp, p) = try + f(sftp, p) + catch err + show(err) + isa(err, IOError) || rethrow() + try + onerror(err) + catch err2 + close(chnl, err2) + end + return + end + + tryf( f, p) = try f(p) catch err + show(err) isa(err, IOError) || rethrow() try onerror(err) catch err2 + close(chnl, err2) end return end - content = tryf(readdir, root) + + content = tryf2(sftpstat2, sftp , root) + content === nothing && return - dirs = Vector{eltype(content)}() - files = Vector{eltype(content)}() - for name in content - path = joinpath(root, name) + dirs = Vector{String}() + files = Vector{String}() + for statstruct in content + + name = statstruct.desc + + path = myjoinpath(root, name) + + + isadir = tryf(isdir, statstruct) + # If we're not following symlinks, then treat all symlinks as files - if (!follow_symlinks && something(tryf(islink, path), true)) || !something(tryf(isdir, path), false) + if (!follow_symlinks && something(tryf(islink, statstruct), true)) || !something(tryf(isdir, statstruct), false) push!(files, name) else push!(dirs, name) @@ -330,7 +392,7 @@ function walkdir(sftp::SFTP, dir; topdown=true, follow_symlinks=false, onerror=t push!(chnl, (root, dirs, files)) end for dir in dirs - _walkdir(chnl, joinpath(root, dir)) + _walkdir(chnl, myjoinpath(root, dir)) end if !topdown push!(chnl, (root, dirs, files)) @@ -346,6 +408,16 @@ end Base.broadcastable(sftp::SFTP) = Ref(sftp) + +""" + islink(path) -> Bool + +Return `true` if `path` is a symbolic link, `false` otherwise. +""" +Base.islink(st::SFTPStatStruct) = filemode(st) & 0xf000 == 0xa000 + + + """ Base.isdir(st::SFTPStatStruct) @@ -443,14 +515,23 @@ function makeStruct(stats::Vector{String})::SFTPStatStruct SFTPStatStruct(stats[9], parseMode(stats[1]), parse(Int64, stats[2]), stats[3], stats[4], parse(Int64, stats[5]), parseDate(stats[6], stats[7], stats[8])) end - """ sftpstat(sftp::SFTP) Like Julia stat, but returns a Vector of SFTPStatStructs. Note that you can only run this on directories. Can be used for checking if a file was modified, and much more. """ -function sftpstat(sftp::SFTP) + +sftpstat(sftp::SFTP) = sftpstat(sftp::SFTP, ".") + + +""" +sftpstat(sftp::SFTP, path::AbstractString) + +Like Julia stat, but returns a Vector of SFTPStatStructs. Note that you can only run this on directories. Can be used for checking if a file was modified, and much more. + +""" +function sftpstat(sftp::SFTP, path::AbstractString) sftp.downloader.easy_hook = (easy, info) -> begin @@ -461,17 +542,18 @@ function sftpstat(sftp::SFTP) output = nothing try - uriString = string(sftp.uri) - if !endswith(uriString, "/") - uriString = uriString * "/" - sftp.uri = URI(uriString) + + if !isdirpath(path) + path = path * "/" end - + newUrl = resolvereference(sftp.uri,sftpescapepath(path)) + + io = IOBuffer(); try - output = Downloads.download(uriString, io; sftp.downloader) + output = Downloads.download(string(newUrl), io; sftp.downloader) finally @@ -649,10 +731,10 @@ function Base.cd(sftp::SFTP, dir::AbstractString) newUrl = resolvereference(oldUrl,sftpescapepath(dir)) - show(newUrl) + #show(newUrl) sftp.uri = newUrl - show(sftp.uri) + #show(sftp.uri) readdir(sftp) catch e sftp.uri = oldUrl