Reputation: 237
I am working on a client iOS app written in Swift which let users transfer files on FTP server. I know Apple offers APIs to do this, but they are deprecated since 2016 and I need an alternative. I know also FTP is not secure, but I have to do it this way. My goal is to open a Socket through the server and send commands to it in order to transfer the file. I'm using BlueSocket but I can't get it. Here's my code:
do {
let socket = try Socket.create()
try socket.connect(to: "127.0.0.1", port: 21)
let answer = try socket.readString()
print(answer!)
try socket.write(from: "USER test\r\n")
let userAnswer = try socket.readString()
print(userAnswer!)
try socket.write(from: "PASS 0000\r\n")
let passwordAnswer = try socket.readString()
print(passwordAnswer!)
try socket.write(from: "PASV\r\n")
let passiveModeAnswer = try socket.readString()
print(passiveModeAnswer!)
try socket.write(from: "STOR myFile.txt\r\n")
let storAnswer = try socket.readString()
print(storAnswer!)
/* CODE FOR SENDING FILE BELOW */
} catch let error as NSError {
print(error.debugDescription)
}
I'm currently working on my local server and I login with success. Server is ready to accept data. I see this in the console:
150 Ok to receive data.
Here's the code I'm using to send the file:
guard let path = Bundle.main.path(forResource: "myFile", ofType: "txt"),
let fileInputStream = InputStream(fileAtPath: path)
else {
return
}
fileInputStream.open()
var buffer = [UInt8](repeating: 0, count: 1024)
var bytesRead = 0
var fileData = Data()
while fileInputStream.hasBytesAvailable {
bytesRead = fileInputStream.read(&buffer, maxLength: buffer.count)
fileData.append(buffer, count: bytesRead)
}
try socket.write(from: fileData)
let fileAnswer = try socket.readString()
print(fileAnswer!)
fileInputStream.close()
socket.close()
After the execution I see "myFile.txt" in my local server, but it's empty. The original file is stored in Xcode: it is a 13 byte file (a simple text file). I see with a breakpoint that bytesRead
changes to 13 and fileData
is valued. I don't receive any response from socket after the try socket.write(from: fileData)
instruction but only this error message: Error code: -9982(0x-26FE), Interrupted system call
. Is something missing or wrong? Am I doing well to achieve my goal?
As Martin correctly said, I've parsed the result of the PASV answer, here's the code:
guard let path = Bundle.main.path(forResource: "myFile", ofType: "txt"),
let fileInputStream = InputStream(fileAtPath: path)
else {
return
}
fileInputStream.open()
guard let pasvAnswer = passiveModeAnswer else {
return
}
var editedAnswer = String(pasvAnswer.dropFirst(26))
editedAnswer = editedAnswer.replacingOccurrences(of: ")", with: "")
editedAnswer = editedAnswer.replacingOccurrences(of: "(", with: "")
editedAnswer = editedAnswer.replacingOccurrences(of: ",", with: ".")
editedAnswer = String(editedAnswer.dropLast())
let cmps = editedAnswer.components(separatedBy: ".")
let dataHost = cmps[0] + "." + cmps[1] + "." + cmps[2] + "." + cmps[3]
let dataPort = ((Int(cmps[4]))!<<8) + Int(cmps[5])!
print("dataHost: \(dataHost), dataPort: \(dataPort)")
try socket.write(from: "STOR myFile.txt\r\n")
let storAnswer = try socket.readString()
print(storAnswer!)
let dataSocket = try Socket.create()
try dataSocket.connect(to: dataHost, port: Int32(dataPort))
var buffer = [UInt8](repeating: 0, count: 1024)
var bytesRead = 0
var fileData = Data()
while fileInputStream.hasBytesAvailable {
bytesRead = fileInputStream.read(&buffer, maxLength: buffer.count)
fileData.append(buffer, count: bytesRead)
}
try dataSocket.write(from: fileData)
let fileAnswer = try dataSocket.readString()
print(fileAnswer!)
fileInputStream.close()
dataSocket.close()
socket.close()
I observed that file is completely transferred only when i kill the app. Any thoughts?
Upvotes: 1
Views: 1190
Reputation: 202474
You have to parse the PASV
response (passiveModeAnswer
). And connect to the provided IP address and port and send the file contents to the new connection.
Upvotes: 1