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 Network
|
||||
import System
|
||||
|
||||
import ImageIO
|
||||
import BigInt
|
||||
import SwiftECC
|
||||
|
||||
@@ -64,19 +64,23 @@ class InboundNearbyConnection: NearbyConnection {
|
||||
try processConnectionResponseFrame(frame)
|
||||
default:
|
||||
|
||||
var smsg: Securemessage_SecureMessage? = nil
|
||||
|
||||
do {
|
||||
let smsg = try Securemessage_SecureMessage(serializedBytes: frameData)
|
||||
try decryptAndProcessReceivedSecureMessage(smsg)
|
||||
smsg = try Securemessage_SecureMessage(serializedBytes: frameData)
|
||||
} catch {
|
||||
|
||||
log("Error deserializing secure message (probably due to packet filter)")
|
||||
lastError = NearbyError.packetFilterError
|
||||
protocolError()
|
||||
}
|
||||
|
||||
if let smsg = smsg {
|
||||
try decryptAndProcessReceivedSecureMessage(smsg)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
lastError = error
|
||||
log("Deserialization error: \(error) in state \(currentState).")
|
||||
log("Error receiving frame: \(error) in state \(currentState).")
|
||||
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") }
|
||||
|
||||
if frame.payloadChunk.body.count > 0 {
|
||||
fileInfo.fileHandle?.write(frame.payloadChunk.body)
|
||||
transferredFiles[id]!.bytesTransferred += Int64(frame.payloadChunk.body.count)
|
||||
fileInfo.progress?.completedUnitCount = transferredFiles[id]!.bytesTransferred
|
||||
do {
|
||||
try fileInfo.fileHandle?.write(contentsOf: frame.payloadChunk.body)
|
||||
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 {
|
||||
try fileInfo.fileHandle?.close()
|
||||
@@ -518,6 +528,9 @@ public class SaveFilesManager {
|
||||
public func registerFileFinishedDownloading(_ fileURL: URL) {
|
||||
filesFinishedDownloading.append(fileURL)
|
||||
filesFinishedDownloadingSinceLastRun.append(fileURL)
|
||||
|
||||
// read out EXIF timestamp and manually set file creation date to it
|
||||
applyEXIFTimestamps(at: fileURL)
|
||||
}
|
||||
|
||||
public func movePendingFilesToTarget() {
|
||||
@@ -633,6 +646,53 @@ public class SaveFilesManager {
|
||||
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 {
|
||||
|
||||