Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8837d0e0aa | ||
|
|
2939990607 | ||
|
|
404e25d3bd | ||
|
|
23705c5e26 | ||
|
|
73e86ff65f | ||
|
|
7403e70893 | ||
|
|
34d677fb92 | ||
|
|
da10b8bbb8 | ||
|
|
ff08266e44 | ||
|
|
b6fdf61430 | ||
|
|
d743a5039a | ||
|
|
f416f8f098 | ||
|
|
90e3e23905 |
No files matched your search
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 488 KiB After Width: | Height: | Size: 488 KiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 527 KiB After Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 488 KiB After Width: | Height: | Size: 488 KiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 527 KiB After Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 752 KiB After Width: | Height: | Size: 752 KiB |
|
Before Width: | Height: | Size: 527 KiB After Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 752 KiB After Width: | Height: | Size: 752 KiB |
|
Before Width: | Height: | Size: 527 KiB After Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
@@ -1,147 +0,0 @@
|
|||||||
//
|
|
||||||
// Logger.swift
|
|
||||||
// QuickDrop
|
|
||||||
//
|
|
||||||
// Created by Leon Böttger on 26.02.25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import os
|
|
||||||
|
|
||||||
fileprivate let loggerQueue = DispatchQueue(label: "loggerQueue")
|
|
||||||
|
|
||||||
/// Manages logging functionality
|
|
||||||
final class LogManager {
|
|
||||||
|
|
||||||
/// The URL of the log file
|
|
||||||
let logFileURL: URL?
|
|
||||||
|
|
||||||
/// The private initializer
|
|
||||||
private init() {
|
|
||||||
|
|
||||||
/// Get documents directory
|
|
||||||
let directory = getDocumentsDirectory()
|
|
||||||
|
|
||||||
/// Create file name from date
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateFormat = "dd-MM-yyyy"
|
|
||||||
let dateString = formatter.string(from: Date())
|
|
||||||
let fileName = "\(dateString).log"
|
|
||||||
|
|
||||||
/// Create file URL from file name
|
|
||||||
self.logFileURL = directory.appendingPathComponent(fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The shared instance.
|
|
||||||
public static let sharedInstance = LogManager()
|
|
||||||
@Published var loggingEnabled = true
|
|
||||||
|
|
||||||
|
|
||||||
/// Writes a specified string to the log file.
|
|
||||||
func writeToFile(string: String) {
|
|
||||||
|
|
||||||
/// Make sure the URL is not nil
|
|
||||||
guard let logFileURL = logFileURL else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get current timestamp
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateFormat = "HH:mm:ss"
|
|
||||||
let timestamp = formatter.string(from: Date())
|
|
||||||
|
|
||||||
/// Create data from string and timestamp
|
|
||||||
guard let data = (timestamp + " - " + string + "\n").data(using: .utf8) else { return }
|
|
||||||
|
|
||||||
/// a log file exists already
|
|
||||||
if FileManager.default.fileExists(atPath: logFileURL.path) {
|
|
||||||
|
|
||||||
/// we try to get a handle to write to the log file
|
|
||||||
if let file = try? FileHandle(forWritingTo: logFileURL) {
|
|
||||||
|
|
||||||
/// go to end of file
|
|
||||||
file.seekToEndOfFile()
|
|
||||||
|
|
||||||
/// write string to end
|
|
||||||
file.write(data)
|
|
||||||
|
|
||||||
///close file
|
|
||||||
file.closeFile()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/// Clean up older logs
|
|
||||||
deleteLogs()
|
|
||||||
|
|
||||||
/// create new log file
|
|
||||||
try? data.write(to: logFileURL, options: .atomicWrite)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogString() -> String {
|
|
||||||
|
|
||||||
if let logFileURL = logFileURL {
|
|
||||||
let fileURL = URL(fileURLWithPath: logFileURL.path)
|
|
||||||
do {
|
|
||||||
let content = try String(contentsOf: fileURL, encoding: .utf8)
|
|
||||||
return String(content)
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
log("[LUI] Error reading file: \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deletes all log files.
|
|
||||||
func deleteLogs() {
|
|
||||||
|
|
||||||
log("[LUI] Deleting all logs...")
|
|
||||||
|
|
||||||
let fileManager = FileManager.default
|
|
||||||
|
|
||||||
let documentsDirectory = getDocumentsDirectory()
|
|
||||||
|
|
||||||
guard let allFiles = try? fileManager.contentsOfDirectory(at: documentsDirectory, includingPropertiesForKeys: nil) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let logFiles = allFiles.filter { $0.pathExtension.elementsEqual("log") }
|
|
||||||
|
|
||||||
for file in logFiles {
|
|
||||||
|
|
||||||
do {
|
|
||||||
log("[LUI] Removing \(file.description)")
|
|
||||||
try fileManager.removeItem(at: file)
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
print(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Logs a specified string.
|
|
||||||
public func log(_ text: String) {
|
|
||||||
|
|
||||||
let logger = LogManager.sharedInstance
|
|
||||||
|
|
||||||
if logger.loggingEnabled {
|
|
||||||
loggerQueue.async {
|
|
||||||
print(text)
|
|
||||||
|
|
||||||
// Weird bug?
|
|
||||||
#if !os(tvOS)
|
|
||||||
logger.writeToFile(string: text)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns the URL of the documents directory.
|
|
||||||
func getDocumentsDirectory() -> URL {
|
|
||||||
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ import CryptoKit
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Network
|
import Network
|
||||||
import System
|
import System
|
||||||
|
import ImageIO
|
||||||
import BigInt
|
import BigInt
|
||||||
import SwiftECC
|
import SwiftECC
|
||||||
|
|
||||||
@@ -64,19 +64,23 @@ class InboundNearbyConnection: NearbyConnection {
|
|||||||
try processConnectionResponseFrame(frame)
|
try processConnectionResponseFrame(frame)
|
||||||
default:
|
default:
|
||||||
|
|
||||||
do {
|
var smsg: Securemessage_SecureMessage? = nil
|
||||||
let smsg = try Securemessage_SecureMessage(serializedBytes: frameData)
|
|
||||||
try decryptAndProcessReceivedSecureMessage(smsg)
|
|
||||||
} catch {
|
|
||||||
|
|
||||||
|
do {
|
||||||
|
smsg = try Securemessage_SecureMessage(serializedBytes: frameData)
|
||||||
|
} catch {
|
||||||
log("Error deserializing secure message (probably due to packet filter)")
|
log("Error deserializing secure message (probably due to packet filter)")
|
||||||
lastError = NearbyError.packetFilterError
|
lastError = NearbyError.packetFilterError
|
||||||
protocolError()
|
protocolError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let smsg = smsg {
|
||||||
|
try decryptAndProcessReceivedSecureMessage(smsg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
lastError = error
|
lastError = error
|
||||||
log("Deserialization error: \(error) in state \(currentState).")
|
log("Error receiving frame: \(error) in state \(currentState).")
|
||||||
protocolError()
|
protocolError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,9 +121,15 @@ class InboundNearbyConnection: NearbyConnection {
|
|||||||
guard currentOffset + Int64(frame.payloadChunk.body.count) <= fileInfo.meta.size else { throw NearbyError.protocolError("Transferred file size exceeds previously specified value") }
|
guard currentOffset + Int64(frame.payloadChunk.body.count) <= fileInfo.meta.size else { throw NearbyError.protocolError("Transferred file size exceeds previously specified value") }
|
||||||
|
|
||||||
if frame.payloadChunk.body.count > 0 {
|
if frame.payloadChunk.body.count > 0 {
|
||||||
fileInfo.fileHandle?.write(frame.payloadChunk.body)
|
do {
|
||||||
transferredFiles[id]!.bytesTransferred += Int64(frame.payloadChunk.body.count)
|
try fileInfo.fileHandle?.write(contentsOf: frame.payloadChunk.body)
|
||||||
fileInfo.progress?.completedUnitCount = transferredFiles[id]!.bytesTransferred
|
transferredFiles[id]!.bytesTransferred += Int64(frame.payloadChunk.body.count)
|
||||||
|
fileInfo.progress?.completedUnitCount = transferredFiles[id]!.bytesTransferred
|
||||||
|
} catch {
|
||||||
|
log("Error occurred during writing file: \(error.localizedDescription)")
|
||||||
|
|
||||||
|
throw NearbyError.protocolError(error.localizedDescription)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (frame.payloadChunk.flags & 1) == 1 {
|
else if (frame.payloadChunk.flags & 1) == 1 {
|
||||||
try fileInfo.fileHandle?.close()
|
try fileInfo.fileHandle?.close()
|
||||||
@@ -518,6 +528,9 @@ public class SaveFilesManager {
|
|||||||
public func registerFileFinishedDownloading(_ fileURL: URL) {
|
public func registerFileFinishedDownloading(_ fileURL: URL) {
|
||||||
filesFinishedDownloading.append(fileURL)
|
filesFinishedDownloading.append(fileURL)
|
||||||
filesFinishedDownloadingSinceLastRun.append(fileURL)
|
filesFinishedDownloadingSinceLastRun.append(fileURL)
|
||||||
|
|
||||||
|
// read out EXIF timestamp and manually set file creation date to it
|
||||||
|
applyEXIFTimestamps(at: fileURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func movePendingFilesToTarget() {
|
public func movePendingFilesToTarget() {
|
||||||
@@ -633,6 +646,53 @@ public class SaveFilesManager {
|
|||||||
fatalError("Failed to get downloads directory: \(error)")
|
fatalError("Failed to get downloads directory: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the EXIF original date of an image file, adjusted to the current time zone.
|
||||||
|
private func exifOriginalDate(from url: URL) -> Date? {
|
||||||
|
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil),
|
||||||
|
let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any],
|
||||||
|
let exif = properties[kCGImagePropertyExifDictionary] as? [CFString: Any],
|
||||||
|
let dateString = exif[kCGImagePropertyExifDateTimeOriginal] as? String
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy:MM:dd HH:mm:ss"
|
||||||
|
formatter.timeZone = TimeZone.current // Use the current device time zone
|
||||||
|
return formatter.date(from: dateString)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Applies EXIF timestamps to a given file or directory.
|
||||||
|
private func applyEXIFTimestamps(at url: URL) {
|
||||||
|
let fm = FileManager.default
|
||||||
|
var isDir: ObjCBool = false
|
||||||
|
guard fm.fileExists(atPath: url.path, isDirectory: &isDir) else { return }
|
||||||
|
|
||||||
|
if isDir.boolValue {
|
||||||
|
// It's a directory — enumerate files
|
||||||
|
if let enumerator = fm.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey]) {
|
||||||
|
for case let fileURL as URL in enumerator {
|
||||||
|
guard (try? fileURL.resourceValues(forKeys: [.isRegularFileKey]))?.isRegularFile == true else { continue }
|
||||||
|
applyEXIFTimestamp(to: fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It's a single file
|
||||||
|
applyEXIFTimestamp(to: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies EXIF timestamp (if available) to a single file
|
||||||
|
private func applyEXIFTimestamp(to fileURL: URL) {
|
||||||
|
guard let exifDate = exifOriginalDate(from: fileURL) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the "creation date"
|
||||||
|
try? FileManager.default.setAttributes([.creationDate: exifDate], ofItemAtPath: fileURL.path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Data {
|
extension Data {
|
||||||
|
|||||||