amosavian / FileProvider

FileManager replacement for Local, iCloud and Remote (WebDAV/FTP/Dropbox/OneDrive) files -- Swift
MIT License
63 stars 16 forks source link

FTP Provider with serverTrustPolicy disabled fails to download file #162

Open ghost opened 4 years ago

ghost commented 4 years ago

I'm trying to download a file from a remote FTPS server but I get the following error:

File Provider <FilesProvider.FTPFileProvider: 0x283c23900> shouldDoOperation Copy with action Copying and destination file:///private/var/mobile/Containers/Data/Application/90AF4202-18C1-4A41-B461-4FB262FD39B9/tmp/B13A8110-C919-48E4-8BD9-E684929310C0.tmp
2020-05-27 14:35:39.372289+0200 MyApp[548:100799] [] nw_socket_handle_socket_event [C13:1] Socket SO_ERROR [54: Connection reset by peer]
2020-05-27 14:35:39.595959+0200 MyApp[548:99892] CFNetwork SSLHandshake failed (-9806)
2020-05-27 14:35:39.596380+0200 MyApp[548:99892] TCP Conn 0x28274f540 SSLHandshake failed (-9806)
File Provider <FilesProvider.FTPFileProvider: 0x283c23900> Failed for operation Copy with action Copying and destination file:///private/var/mobile/Containers/Data/Application/90AF4202-18C1-4A41-B461-4FB262FD39B9/tmp/B13A8110-C919-48E4-8BD9-E684929310C0.tmp
Throwing Error: Error Domain=NSOSStatusErrorDomain Code=-9806 "(null)" UserInfo={_kCFStreamErrorCodeKey=-9806, _kCFStreamErrorDomainKey=3}

The provider looks like:

guard let url = URL(string: "ftps://X.X.X.X") else { return } // I have to use an IP address instead of a domain
var provider = FTPFileProvider(baseURL: url, mode: .default, credential: credential, cache: .none)
provider.delegate = self
provider.fileOperationDelegate = self // This delegate is only for print the first line of the error before returning true
provider.serverTrustPolicy = .disableEvaluation

With that provider I successfully login into the FTP server and perform some search. For example:

provider.searchFiles(path: remotePath, recursive: false, query: predicate, foundItemHandler: { (file) in
      print("File found with name: \(file.name)")
    }, completionHandler: { (list, error) in
      if error != nil {
        DispatchQueue.main.async {
          onError(error!)
        }
      } else {
        var files:[String] = []
        for f in list {
          (f.isRegularFile) ? files.append(f.name) : nil
        }
        DispatchQueue.main.async {
          onSucess(files)
        }
      }
    })

That file search show the following warning in console:

2020-05-27 14:45:51.831812+0200 MyApp[555:102153] [] nw_socket_handle_socket_event [C4:1] Socket SO_ERROR [54: Connection reset by peer]

But I get a successful output where onSuccess(files) returns:

["20200527-093234-28346646454.pdf", "20200527-105409-28346646454.pdf"]

When trying to download one of the files is when I get the error. For download I'm using the following:

provider.copyItem(path: "\(remotePath)/\(file)", to: localPath.absoluteString, overwrite: true) { (error) in
      if error != nil {
        DispatchQueue.main.async {
          onError(error!)
        }
      } else {
        DispatchQueue.main.async {
          onSuccess(localPath)
        }
      }
    }

My Info.plistlooks like:

<key>NSAppTransportSecurity</key>
  <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSAllowsLocalNetworking</key>
    <true/>
  </dict>

Some facts:

Why login, listing or searching files works but I get that error when trying to download?
Hope you can give it a look.

ghost commented 4 years ago

With plain FTP it works perfect but that is obvious. I keep wondering why login and list/search files works but download don't…

imoyu-tech commented 4 years ago

I have the same problem. Have you solved it?

ghost commented 4 years ago

I have the same problem. Have you solved it?

No, I still have the problem when using the IP address. The DNS entry was created so now I can download.

jlazarow commented 3 years ago

This is a weird bug, but it seems to be caused by the nesting of "getting file attributes", then "getting file data" during the download. I remedied up by making a separate call to "attributesOfItem", and then a separate call to "contents" with the resulting attributes. I think it comes down to some re-use of sockets that ends up confusing the SSL properties of the socket.