aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Hotline/Hotline/HotlineFileClient.swift110
-rw-r--r--Hotline/Models/Hotline.swift37
-rw-r--r--Hotline/Models/TransferInfo.swift1
-rw-r--r--Hotline/macOS/FilesView.swift3
4 files changed, 136 insertions, 15 deletions
diff --git a/Hotline/Hotline/HotlineFileClient.swift b/Hotline/Hotline/HotlineFileClient.swift
index 5b2d05a..8202a61 100644
--- a/Hotline/Hotline/HotlineFileClient.swift
+++ b/Hotline/Hotline/HotlineFileClient.swift
@@ -1,5 +1,6 @@
import Foundation
import Network
+import UniformTypeIdentifiers
enum HotlineFileClientError: Error {
case failedToConnect
@@ -39,7 +40,8 @@ enum HotlineFileTransferStage: Int {
case fileForkHeader = 2
case fileInfoFork = 3
case fileDataFork = 4
- case fileUnsupportedFork = 5
+ case fileResourceFork = 5
+ case fileUnsupportedFork = 6
}
struct HotlineFileHeader {
@@ -186,6 +188,7 @@ class HotlineFileClient {
private var transferStage: HotlineFileTransferStage = .fileHeader
private var fileBytes = Data()
+ private var fileResourceBytes = Data()
private var fileHeader: HotlineFileHeader? = nil
private var fileCurrentForkHeader: HotlineFileForkHeader? = nil
@@ -225,6 +228,16 @@ class HotlineFileClient {
return
}
+ self.filePath = nil
+ self.connect()
+ }
+
+ func downloadToFile(to fileURL: URL) {
+ guard self.status == .unconnected else {
+ return
+ }
+
+ self.filePath = fileURL.path
self.connect()
}
@@ -395,7 +408,11 @@ class HotlineFileClient {
}
else if forkHeader.forkType == "MACR".fourCharCode() {
print("RESOURCE FORK!")
- self.transferStage = .fileUnsupportedFork
+ self.fileResourceBytes = Data()
+// if self.prepareResourceFile() {
+// print("BOOYAH")
+// }
+ self.transferStage = .fileResourceFork
}
else {
print("UNKNOWN FORK??")
@@ -418,6 +435,7 @@ class HotlineFileClient {
print("INFO FORK STUFF:", infoFork)
+ // Create file in Downloads folder if we don't have a destination already.
if !self.prepareDownloadFile(name: infoFork.name) {
print("FAILED TO CREATE FILE ON DISK")
}
@@ -458,6 +476,37 @@ class HotlineFileClient {
}
}
}
+ case .fileResourceFork:
+ if self.fileBytes.count > 0 {
+// if let f = self.fileResourceHandle {
+// do {
+ var dataToWrite = self.fileBytes
+
+ if dataToWrite.count >= self.fileCurrentForkBytesLeft {
+ dataToWrite = self.fileBytes.subdata(in: 0..<self.fileCurrentForkBytesLeft)
+ self.fileBytes.removeSubrange(0..<self.fileCurrentForkBytesLeft)
+
+ self.transferStage = .fileForkHeader
+ self.fileCurrentForkBytesLeft = 0
+ self.fileCurrentForkHeader = nil
+
+ keepProcessing = true
+ }
+ else {
+ self.fileCurrentForkBytesLeft -= dataToWrite.count
+ self.fileBytes = Data()
+ }
+
+ print("WRITING TO RESOURCE FORK", dataToWrite.count)
+
+ self.fileResourceBytes.append(dataToWrite)
+// try f.write(contentsOf: dataToWrite)
+// }
+// catch {
+// print("DOWNLOAD WRITE ERROR", error)
+// }
+// }
+ }
case .fileUnsupportedFork:
if self.fileBytes.count > 0 {
var dataToWrite = self.fileBytes
@@ -486,15 +535,14 @@ class HotlineFileClient {
return
}
else {
- print("FILE COMPLETE")
- if let h = self.fileHandle {
- try? h.close()
- self.fileHandle = nil
+ self.invalidate()
+
+ if self.fileResourceBytes.count > 0 {
+ let _ = self.writeResourceFork()
+ self.fileResourceBytes = Data()
}
- self.fileBytes = Data()
self.status = .completed
- self.invalidate()
if let downloadPath = self.filePath {
DispatchQueue.main.sync {
@@ -561,11 +609,28 @@ class HotlineFileClient {
// MARK: - Utility
- private func prepareDownloadFile(name: String) -> Bool {
- let folderURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
- let filePath = folderURL.generateUniqueFilePath(filename: name)
+ private func writeResourceFork() -> Bool {
+ guard let filePath = self.filePath else {
+ return false
+ }
+
+ var resolvedFileURL = URL(filePath: filePath)
+ resolvedFileURL.resolveSymlinksInPath()
+
+ let resourceFilePath = resolvedFileURL.appendingPathComponent("..namedfork/rsrc")
+
+ do {
+ try self.fileResourceBytes.write(to: resourceFilePath)
+ }
+ catch {
+ return false
+ }
- if FileManager.default.createFile(atPath: filePath, contents: nil) {
+ return true
+ }
+
+ private func prepareDownloadFile(name: String) -> Bool {
+ if let filePath = self.filePath {
if let h = FileHandle(forWritingAtPath: filePath) {
self.filePath = filePath
self.fileHandle = h
@@ -573,6 +638,27 @@ class HotlineFileClient {
return true
}
}
+ else {
+ let folderURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
+ let filePath = folderURL.generateUniqueFilePath(filename: name)
+
+ var fileAttributes: [FileAttributeKey : Any] = [:]
+ if let creatorCode = self.fileInfoFork?.creator {
+ fileAttributes[.hfsCreatorCode] = creatorCode as NSNumber
+ }
+ if let typeCode = self.fileInfoFork?.type {
+ fileAttributes[.hfsTypeCode] = typeCode as NSNumber
+ }
+
+ if FileManager.default.createFile(atPath: filePath, contents: nil, attributes: fileAttributes) {
+ if let h = FileHandle(forWritingAtPath: filePath) {
+ self.filePath = filePath
+ self.fileHandle = h
+ self.fileProgress?.fileURL = URL(filePath: filePath).resolvingSymlinksInPath()
+ return true
+ }
+ }
+ }
return false
}
diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift
index f15bbb6..814ade5 100644
--- a/Hotline/Models/Hotline.swift
+++ b/Hotline/Models/Hotline.swift
@@ -473,6 +473,42 @@ final class Hotline: HotlineClientDelegate, HotlineFileClientDelegate {
}
}
+ @MainActor func downloadFileTo(url fileURL: URL, fileName: String, path: [String], progress progressCallback: ((TransferInfo, Double) -> Void)? = nil, complete callback: ((TransferInfo, URL) -> Void)? = nil) {
+ var fullPath: [String] = []
+ if path.count > 1 {
+ fullPath = Array(path[0..<path.count-1])
+ }
+
+ self.client.sendDownloadFile(name: fileName, path: fullPath) { [weak self] success, downloadReferenceNumber, downloadTransferSize, downloadFileSize, downloadWaitingCount in
+ print("GOT DOWNLOAD REPLY:")
+ print("\tSUCCESS?", success)
+ print("\tTRANSFER SIZE: \(downloadTransferSize.debugDescription)")
+ print("\tFILE SIZE: \(downloadFileSize.debugDescription)")
+ print("\tREFERENCE NUM: \(downloadReferenceNumber.debugDescription)")
+ print("\tWAITING COUNT: \(downloadWaitingCount.debugDescription)")
+
+ if
+ let self = self,
+ let address = self.server?.address,
+ let port = self.server?.port,
+ let referenceNumber = downloadReferenceNumber,
+ let transferSize = downloadTransferSize {
+
+ print("DOWNLOADING TO MEMORY")
+ let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .file)
+ fileClient.delegate = self
+ self.downloads.append(fileClient)
+
+ let transfer = TransferInfo(id: referenceNumber, title: fileName, size: UInt(transferSize))
+ transfer.downloadCallback = callback
+ transfer.progressCallback = progressCallback
+ self.transfers.append(transfer)
+
+ fileClient.downloadToFile(to: fileURL)
+ }
+ }
+ }
+
@MainActor func previewFile(_ fileName: String, path: [String], complete callback: ((PreviewFileInfo?) -> Void)? = nil) {
var fullPath: [String] = []
if path.count > 1 {
@@ -748,6 +784,7 @@ final class Hotline: HotlineClientDelegate, HotlineFileClientDelegate {
if let transfer = self.transfers.first(where: { $0.id == reference }) {
transfer.progress = progress
transfer.timeRemaining = timeRemaining
+ transfer.progressCallback?(transfer, progress)
}
case .failed(_):
if let i = self.downloads.firstIndex(where: { $0.referenceNumber == reference }) {
diff --git a/Hotline/Models/TransferInfo.swift b/Hotline/Models/TransferInfo.swift
index 546efd3..8b8d97a 100644
--- a/Hotline/Models/TransferInfo.swift
+++ b/Hotline/Models/TransferInfo.swift
@@ -14,6 +14,7 @@ class TransferInfo: Identifiable, Equatable, Hashable {
// For file based transfers (i.e. not previews)
var fileURL: URL? = nil
+ var progressCallback: ((TransferInfo, Double) -> Void)? = nil
var downloadCallback: ((TransferInfo, URL) -> Void)? = nil
var previewCallback: ((TransferInfo, Data) -> Void)? = nil
diff --git a/Hotline/macOS/FilesView.swift b/Hotline/macOS/FilesView.swift
index b9f15db..397feb3 100644
--- a/Hotline/macOS/FilesView.swift
+++ b/Hotline/macOS/FilesView.swift
@@ -42,9 +42,6 @@ struct FileView: View {
}
else {
FileIconView(filename: file.name)
-// .resizable()
-// .aspectRatio(contentMode: .fit)
-// .scaledToFill()
.frame(width: 16, height: 16)
}
}