Use distinct directories when forwarding
This commit is contained in:
parent
ffe0dd7070
commit
86751158f3
@ -68,7 +68,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
clearAppropriateNotificationsAndRestoreBadgeCount()
|
||||
|
||||
// On every activation, clear old temp directories.
|
||||
ClearOldTemporaryDirectories()
|
||||
OWSFileSystem.clearOldTemporaryDirectories()
|
||||
|
||||
// Ensure that all windows have the correct frame.
|
||||
AppEnvironment.shared.windowManagerRef.updateWindowFrames()
|
||||
|
||||
@ -16,7 +16,6 @@ enum SignalAttachmentCloner {
|
||||
throw OWSAssertionError("Missing dataUTI.")
|
||||
}
|
||||
|
||||
// Just use a random file name on the decrypted copy; its internal use only.
|
||||
let decryptedCopyUrl = try attachment.attachmentStream.makeDecryptedCopy(
|
||||
filename: attachment.reference.sourceFilename,
|
||||
)
|
||||
|
||||
@ -48,10 +48,7 @@ class DeviceTransferOperation: NSObject {
|
||||
|
||||
Logger.warn("Missing file for transfer, it probably disappeared or was otherwise deleted. Sending missing file placeholder.")
|
||||
|
||||
url = URL(
|
||||
fileURLWithPath: UUID().uuidString,
|
||||
relativeTo: URL(fileURLWithPath: OWSTemporaryDirectory(), isDirectory: true),
|
||||
)
|
||||
url = OWSFileSystem.temporaryFileUrl(isAvailableWhileDeviceLocked: false)
|
||||
guard
|
||||
FileManager.default.createFile(
|
||||
atPath: url.path,
|
||||
|
||||
@ -16,8 +16,8 @@ enum AttachmentSaving {
|
||||
static func saveToPhotoLibrary(
|
||||
referencedAttachmentStreams: [ReferencedAttachmentStream],
|
||||
) {
|
||||
let (assetCreationRequests, _) = referencedAttachmentStreams.reduce(
|
||||
into: (requests: [PHAssetCreationRequestType](), filenames: Set<String>()),
|
||||
let assetCreationRequests = referencedAttachmentStreams.reduce(
|
||||
into: [PHAssetCreationRequestType](),
|
||||
) { result, referencedAttachmentStream in
|
||||
let reference = referencedAttachmentStream.reference
|
||||
let attachmentStream = referencedAttachmentStream.attachmentStream
|
||||
@ -29,16 +29,9 @@ enum AttachmentSaving {
|
||||
break
|
||||
}
|
||||
|
||||
let filename = uniqueFilename(
|
||||
sourceFilename: reference.sourceFilename,
|
||||
existingFilenames: &result.filenames,
|
||||
)
|
||||
|
||||
let decryptedFileUrl: URL
|
||||
do {
|
||||
decryptedFileUrl = try attachmentStream.makeDecryptedCopy(
|
||||
filename: filename,
|
||||
)
|
||||
decryptedFileUrl = try attachmentStream.makeDecryptedCopy(filename: reference.sourceFilename)
|
||||
} catch let error {
|
||||
owsFailDebug("Failed to save decrypted copy of attachment for photo library! \(error)")
|
||||
return
|
||||
@ -48,9 +41,9 @@ enum AttachmentSaving {
|
||||
case .invalid, .audio, .file:
|
||||
owsFail("Impossible: checked above!")
|
||||
case .image, .animatedImage:
|
||||
result.requests.append(.imageTempFile(tmpFileUrl: decryptedFileUrl))
|
||||
result.append(.imageTempFile(tmpFileUrl: decryptedFileUrl))
|
||||
case .video:
|
||||
result.requests.append(.videoTempFile(tmpFileUrl: decryptedFileUrl))
|
||||
result.append(.videoTempFile(tmpFileUrl: decryptedFileUrl))
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,38 +163,6 @@ enum AttachmentSaving {
|
||||
|
||||
// MARK: -
|
||||
|
||||
static func uniqueFilename(
|
||||
sourceFilename: String?,
|
||||
existingFilenames: inout Set<String>,
|
||||
) -> String? {
|
||||
if
|
||||
let sourceFilename,
|
||||
existingFilenames.contains(sourceFilename)
|
||||
{
|
||||
// Avoid source filename collisions.
|
||||
let pathExtension = (sourceFilename as NSString).pathExtension
|
||||
let normalizedFilename = (sourceFilename as NSString)
|
||||
.deletingPathExtension
|
||||
.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
var i = 0
|
||||
while true {
|
||||
i += 1
|
||||
var newSourceFilename = normalizedFilename + "_\(i)"
|
||||
newSourceFilename = (newSourceFilename as NSString).appendingPathExtension(pathExtension) ?? newSourceFilename
|
||||
if !existingFilenames.contains(newSourceFilename) {
|
||||
existingFilenames.insert(newSourceFilename)
|
||||
return newSourceFilename
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_ = sourceFilename.map { existingFilenames.insert($0) }
|
||||
return sourceFilename
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private enum PHAssetCreationRequestType {
|
||||
case image(UIImage)
|
||||
case imageTempFile(tmpFileUrl: URL)
|
||||
|
||||
@ -138,8 +138,6 @@ extension Array where Element == ReferencedAttachmentStream {
|
||||
public func asShareableAttachments() throws -> [ShareableAttachment] {
|
||||
var hadUrlType = false
|
||||
var types = [ShareableAttachment.ShareType]()
|
||||
var dedupedSourceFilenames = [String?]()
|
||||
var sourceFilenamesSet = Set<String>()
|
||||
for attachment in self {
|
||||
let shareType = ShareableAttachment.shareType(attachment.attachmentStream)
|
||||
switch shareType {
|
||||
@ -149,23 +147,17 @@ extension Array where Element == ReferencedAttachmentStream {
|
||||
case .image:
|
||||
types.append(.image)
|
||||
}
|
||||
|
||||
let filename = AttachmentSaving.uniqueFilename(
|
||||
sourceFilename: attachment.reference.sourceFilename,
|
||||
existingFilenames: &sourceFilenamesSet,
|
||||
)
|
||||
dedupedSourceFilenames.append(filename)
|
||||
}
|
||||
if hadUrlType {
|
||||
// Once one of them are all file, they all have to be files.
|
||||
types = [ShareableAttachment.ShareType](repeating: .decryptedFileURL, count: self.count)
|
||||
}
|
||||
|
||||
return try zip(zip(self, types), dedupedSourceFilenames).compactMap {
|
||||
let ((attachment, shareType), sourceFilename) = $0
|
||||
return try zip(self, types).compactMap {
|
||||
let (attachment, shareType) = $0
|
||||
return try ShareableAttachment(
|
||||
attachment.attachmentStream,
|
||||
sourceFilename: sourceFilename,
|
||||
sourceFilename: attachment.reference.sourceFilename,
|
||||
shareType: shareType,
|
||||
)
|
||||
}
|
||||
|
||||
@ -257,15 +257,12 @@ class RequestAccountDataReportViewController: OWSTableViewController2 {
|
||||
// In practice, this doesn't work when sharing back into Signal. We don't understand why
|
||||
// but suspect a platform bug (or, at best, an error message that didn't help us figure out
|
||||
// the source of the problem).
|
||||
let temporaryDirUrl = URL(
|
||||
fileURLWithPath: OWSTemporaryDirectory(),
|
||||
).appendingPathComponent(UUID().uuidString)
|
||||
let temporaryFileUrl = temporaryDirUrl.appendingPathComponent(
|
||||
let temporaryFileUrl = OWSFileSystem.temporaryFileUrl(
|
||||
// This isn't localized because the report is *also* not localized.
|
||||
"account-data.\(fileExtension)",
|
||||
isDirectory: false,
|
||||
fileName: "account-data",
|
||||
fileExtension: fileExtension,
|
||||
isAvailableWhileDeviceLocked: false,
|
||||
)
|
||||
OWSFileSystem.ensureDirectoryExists(temporaryDirUrl.path)
|
||||
|
||||
let activityItem: Any
|
||||
let cleanup: () -> Void
|
||||
@ -275,7 +272,7 @@ class RequestAccountDataReportViewController: OWSTableViewController2 {
|
||||
activityItem = temporaryFileUrl
|
||||
cleanup = {
|
||||
do {
|
||||
try OWSFileSystem.deleteFile(url: temporaryDirUrl)
|
||||
try OWSFileSystem.deleteFile(url: temporaryFileUrl)
|
||||
} catch {
|
||||
owsFailBeta("Failed to delete temporary account data report file")
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ class InternalDiskUsageViewController: OWSTableViewController2 {
|
||||
let sharedDataSize = folderSizeRecursive(ofPath: OWSFileSystem.appSharedDataDirectoryPath())
|
||||
|
||||
let bundleSize = folderSizeRecursive(ofPath: Bundle.main.bundlePath)
|
||||
let tmpSize = folderSizeRecursive(ofPath: OWSTemporaryDirectory())
|
||||
let tmpSize = folderSizeRecursive(ofPath: NSTemporaryDirectory())
|
||||
}
|
||||
|
||||
let diskUsage: DiskUsage
|
||||
|
||||
@ -197,7 +197,7 @@ enum DebugLogs {
|
||||
let dateString = dateFormatter.string(from: Date())
|
||||
let logsName = "\(dateString) \(UUID().uuidString)"
|
||||
|
||||
let zipDirUrl = URL(fileURLWithPath: OWSTemporaryDirectory()).appendingPathComponent(logsName)
|
||||
let zipDirUrl = OWSFileSystem.temporaryFileUrl(isAvailableWhileDeviceLocked: false).appendingPathComponent(logsName)
|
||||
let zipDirPath = zipDirUrl.path
|
||||
OWSFileSystem.ensureDirectoryExists(zipDirPath)
|
||||
|
||||
|
||||
@ -951,29 +951,21 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
|
||||
|
||||
// MARK: Temp Directory
|
||||
|
||||
public func ensureDownloadFolder() {
|
||||
private func ensureDownloadFolder() {
|
||||
// We write assets to the temporary directory so that iOS can clean them up.
|
||||
// We try to eagerly clean up these assets when they are no longer in use.
|
||||
|
||||
let tempDirPath = OWSTemporaryDirectory()
|
||||
let tempDirPath = OWSFileSystem.temporaryFilePath(isAvailableWhileDeviceLocked: false)
|
||||
let dirPath = (tempDirPath as NSString).appendingPathComponent(downloadFolderName)
|
||||
do {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
// Try to delete existing folder if necessary.
|
||||
if fileManager.fileExists(atPath: dirPath) {
|
||||
try fileManager.removeItem(atPath: dirPath)
|
||||
downloadFolderPath = dirPath
|
||||
}
|
||||
// Try to create folder if necessary.
|
||||
if !fileManager.fileExists(atPath: dirPath) {
|
||||
try fileManager.createDirectory(
|
||||
atPath: dirPath,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil,
|
||||
)
|
||||
downloadFolderPath = dirPath
|
||||
}
|
||||
try fileManager.createDirectory(
|
||||
atPath: dirPath,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil,
|
||||
)
|
||||
downloadFolderPath = dirPath
|
||||
|
||||
// Don't back up ProxiedContent downloads.
|
||||
OWSFileSystem.protectFileOrFolder(atPath: dirPath)
|
||||
|
||||
@ -10,7 +10,7 @@ import Foundation
|
||||
@objc
|
||||
public class TestAppContext: NSObject, AppContext {
|
||||
public static var testDebugLogsDirPath: String {
|
||||
let dirPath = OWSTemporaryDirectory().appendingPathComponent("TestLogs")
|
||||
let dirPath = OWSFileSystem.temporaryFilePath(isAvailableWhileDeviceLocked: false).appendingPathComponent("TestLogs")
|
||||
OWSFileSystem.ensureDirectoryExists(dirPath)
|
||||
return dirPath
|
||||
}
|
||||
|
||||
@ -5,87 +5,87 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
private let owsTempDir = {
|
||||
let dirPath = NSTemporaryDirectory().appendingPathComponent("ows_temp_\(UUID())")
|
||||
owsPrecondition(OWSFileSystem.ensureDirectoryExists(dirPath, fileProtectionType: .complete))
|
||||
return dirPath
|
||||
}()
|
||||
public enum OWSFileSystem {
|
||||
|
||||
/// Use instead of NSTemporaryDirectory()
|
||||
/// prefer the more restrictice OWSTemporaryDirectory,
|
||||
/// unless the temp data may need to be accessed while the device is locked.
|
||||
public func OWSTemporaryDirectory() -> String {
|
||||
return owsTempDir
|
||||
}
|
||||
private static let tempDirComplete = {
|
||||
let dirPath = NSTemporaryDirectory().appendingPathComponent("ows_temp_\(UUID())")
|
||||
owsPrecondition(OWSFileSystem.ensureDirectoryExists(dirPath, fileProtectionType: .complete))
|
||||
return dirPath
|
||||
}()
|
||||
|
||||
public func OWSTemporaryDirectoryAccessibleAfterFirstAuth() -> String {
|
||||
let dirPath = NSTemporaryDirectory()
|
||||
owsPrecondition(OWSFileSystem.ensureDirectoryExists(dirPath, fileProtectionType: .completeUntilFirstUserAuthentication))
|
||||
return dirPath
|
||||
}
|
||||
private static let tempDirAfterFirstUnlock = {
|
||||
let tmpPath = NSTemporaryDirectory()
|
||||
owsPrecondition(OWSFileSystem.ensureDirectoryExists(tmpPath, fileProtectionType: .completeUntilFirstUserAuthentication))
|
||||
let dirPath = tmpPath.appendingPathComponent("ows_temp_\(UUID())")
|
||||
owsPrecondition(OWSFileSystem.ensureDirectoryExists(dirPath, fileProtectionType: .completeUntilFirstUserAuthentication))
|
||||
return dirPath
|
||||
}()
|
||||
|
||||
private let cleanTmpDispatchQueue = DispatchQueue(label: "org.signal.clean-tmp", qos: .utility)
|
||||
/// > NOTE: We need to call this method on launch _and_ every time the app becomes active,
|
||||
/// > since file protection may prevent it from succeeding in the background.
|
||||
public func ClearOldTemporaryDirectories() {
|
||||
let dispatchTime = DispatchTime.now() + .seconds(3)
|
||||
cleanTmpDispatchQueue.asyncAfter(deadline: dispatchTime, execute: DispatchWorkItem(block: {
|
||||
ClearOldTemporaryDirectoriesSync()
|
||||
}))
|
||||
}
|
||||
private static let cleanTmpDispatchQueue = DispatchQueue(label: "org.signal.clean-tmp", qos: .utility)
|
||||
|
||||
private func ClearOldTemporaryDirectoriesSync() {
|
||||
// Ignore the "current" temp directory.
|
||||
let currentTempDirName = (OWSTemporaryDirectory() as NSString).lastPathComponent
|
||||
|
||||
let thresholdDate = CurrentAppContext().appLaunchTime
|
||||
let dirPath = NSTemporaryDirectory()
|
||||
let fileNames: [String]
|
||||
do {
|
||||
fileNames = try FileManager.default.contentsOfDirectory(atPath: dirPath)
|
||||
} catch {
|
||||
owsFailDebug("contentsOfDirectoryAtPath error: \(error)")
|
||||
return
|
||||
/// We need to call this method on launch AND every time the app becomes
|
||||
/// active because file protection may prevent it from succeeding in the
|
||||
/// background.
|
||||
public static func clearOldTemporaryDirectories() {
|
||||
let dispatchTime = DispatchTime.now() + .seconds(3)
|
||||
cleanTmpDispatchQueue.asyncAfter(deadline: dispatchTime, execute: DispatchWorkItem(block: {
|
||||
_clearOldTemporaryDirectories()
|
||||
}))
|
||||
}
|
||||
for fileName in fileNames {
|
||||
if fileName == currentTempDirName {
|
||||
continue
|
||||
|
||||
private static func _clearOldTemporaryDirectories() {
|
||||
// Ignore the "current" temp directory.
|
||||
let currentTempDirNames = [
|
||||
(tempDirComplete as NSString).lastPathComponent,
|
||||
(tempDirAfterFirstUnlock as NSString).lastPathComponent,
|
||||
]
|
||||
|
||||
let thresholdDate = CurrentAppContext().appLaunchTime
|
||||
let dirPath = NSTemporaryDirectory()
|
||||
let fileNames: [String]
|
||||
do {
|
||||
fileNames = try FileManager.default.contentsOfDirectory(atPath: dirPath)
|
||||
} catch {
|
||||
owsFailDebug("contentsOfDirectoryAtPath error: \(error)")
|
||||
return
|
||||
}
|
||||
|
||||
let filePath = dirPath.appendingPathComponent(fileName)
|
||||
|
||||
// Delete files with either:
|
||||
//
|
||||
// a) "ows_temp" name prefix.
|
||||
// b) modified time before app launch time.
|
||||
if !fileName.hasPrefix("ows_temp") {
|
||||
do {
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: filePath)
|
||||
// Don't delete files which were created in the last N minutes.
|
||||
let mtime = attributes[.modificationDate] as? Date
|
||||
guard let mtime else {
|
||||
Logger.error("failed to get a modification date for file or directory at: \(filePath)")
|
||||
continue
|
||||
}
|
||||
if mtime > thresholdDate {
|
||||
continue
|
||||
}
|
||||
} catch {
|
||||
// This is fine; the file may have been deleted since we found it.
|
||||
Logger.error("Could not get attributes of file or directory at: \(filePath)")
|
||||
for fileName in fileNames {
|
||||
if currentTempDirNames.contains(fileName) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !OWSFileSystem.deleteFileIfExists(filePath) {
|
||||
// This can happen if the app launches before the phone is unlocked.
|
||||
// Clean up will occur when app becomes active.
|
||||
Logger.warn("Could not delete old temp directory: \(filePath)")
|
||||
let filePath = dirPath.appendingPathComponent(fileName)
|
||||
|
||||
// Delete files with either:
|
||||
//
|
||||
// a) "ows_temp" name prefix.
|
||||
// b) modified time before app launch time.
|
||||
if !fileName.hasPrefix("ows_temp_") {
|
||||
do {
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: filePath)
|
||||
// Don't delete files which were created in the last N minutes.
|
||||
let mtime = attributes[.modificationDate] as? Date
|
||||
guard let mtime else {
|
||||
Logger.error("failed to get a modification date for file or directory at: \(filePath)")
|
||||
continue
|
||||
}
|
||||
if mtime > thresholdDate {
|
||||
continue
|
||||
}
|
||||
} catch {
|
||||
// This is fine; the file may have been deleted since we found it.
|
||||
Logger.error("Could not get attributes of file or directory at: \(filePath)")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !OWSFileSystem.deleteFileIfExists(filePath) {
|
||||
// This can happen if the app launches before the phone is unlocked.
|
||||
// Clean up will occur when app becomes active.
|
||||
Logger.warn("Could not delete old temp directory: \(filePath)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum OWSFileSystem {
|
||||
|
||||
@discardableResult
|
||||
private static func protectRecursiveContents(atPath path: String) -> Bool {
|
||||
@ -207,9 +207,6 @@ public enum OWSFileSystem {
|
||||
public static func fileSize(of fileUrl: URL) throws -> UInt64 {
|
||||
return try fileSize(ofPath: fileUrl.path)
|
||||
}
|
||||
}
|
||||
|
||||
extension OWSFileSystem {
|
||||
|
||||
/// - Returns: false iff the directory does not exist and could not be created or setting the file protection type fails
|
||||
@discardableResult
|
||||
@ -226,42 +223,40 @@ extension OWSFileSystem {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension OWSFileSystem {
|
||||
static func fileOrFolderExists(atPath filePath: String) -> Bool {
|
||||
public static func fileOrFolderExists(atPath filePath: String) -> Bool {
|
||||
FileManager.default.fileExists(atPath: filePath)
|
||||
}
|
||||
|
||||
static func fileOrFolderExists(url: URL) -> Bool {
|
||||
public static func fileOrFolderExists(url: URL) -> Bool {
|
||||
fileOrFolderExists(atPath: url.path)
|
||||
}
|
||||
|
||||
static func fileExistsAndIsNotDirectory(atPath filePath: String) -> Bool {
|
||||
public static func fileExistsAndIsNotDirectory(atPath filePath: String) -> Bool {
|
||||
var isDirectory: ObjCBool = false
|
||||
let exists = FileManager.default.fileExists(atPath: filePath, isDirectory: &isDirectory)
|
||||
return exists && !isDirectory.boolValue
|
||||
}
|
||||
|
||||
static func fileExistsAndIsNotDirectory(url: URL) -> Bool {
|
||||
public static func fileExistsAndIsNotDirectory(url: URL) -> Bool {
|
||||
fileExistsAndIsNotDirectory(atPath: url.path)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
static func deleteFile(_ filePath: String) -> Bool {
|
||||
public static func deleteFile(_ filePath: String) -> Bool {
|
||||
deleteFile(filePath, ignoreIfMissing: false)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
static func deleteFileIfExists(_ filePath: String) -> Bool {
|
||||
public static func deleteFileIfExists(_ filePath: String) -> Bool {
|
||||
return deleteFile(filePath, ignoreIfMissing: true)
|
||||
}
|
||||
|
||||
static func deleteFile(url: URL) throws {
|
||||
public static func deleteFile(url: URL) throws {
|
||||
try FileManager.default.removeItem(at: url)
|
||||
}
|
||||
|
||||
static func deleteFileIfExists(url: URL) throws {
|
||||
public static func deleteFileIfExists(url: URL) throws {
|
||||
do {
|
||||
try deleteFile(url: url)
|
||||
} catch POSIXError.ENOENT, CocoaError.fileNoSuchFile {
|
||||
@ -269,7 +264,7 @@ public extension OWSFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
static func moveFile(from fromUrl: URL, to toUrl: URL) throws {
|
||||
public static func moveFile(from fromUrl: URL, to toUrl: URL) throws {
|
||||
guard FileManager.default.fileExists(atPath: fromUrl.path) else {
|
||||
throw OWSAssertionError("Source file does not exist.")
|
||||
}
|
||||
@ -295,7 +290,7 @@ public extension OWSFileSystem {
|
||||
#endif
|
||||
}
|
||||
|
||||
static func copyFile(from fromUrl: URL, to toUrl: URL) throws {
|
||||
public static func copyFile(from fromUrl: URL, to toUrl: URL) throws {
|
||||
guard FileManager.default.fileExists(atPath: fromUrl.path) else {
|
||||
throw OWSAssertionError("Source file does not exist.")
|
||||
}
|
||||
@ -318,7 +313,7 @@ public extension OWSFileSystem {
|
||||
#endif
|
||||
}
|
||||
|
||||
static func recursiveFilesInDirectory(_ dirPath: String) throws -> [String] {
|
||||
public static func recursiveFilesInDirectory(_ dirPath: String) throws -> [String] {
|
||||
owsAssertDebug(!dirPath.isEmpty)
|
||||
|
||||
do {
|
||||
@ -334,86 +329,49 @@ public extension OWSFileSystem {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Temporary Files
|
||||
// MARK: - Temporary Files
|
||||
|
||||
public extension OWSFileSystem {
|
||||
|
||||
static func temporaryFileUrl(
|
||||
fileExtension: String?,
|
||||
public static func temporaryFileUrl(
|
||||
fileName: String? = nil,
|
||||
fileExtension: String? = nil,
|
||||
isAvailableWhileDeviceLocked: Bool,
|
||||
) -> URL {
|
||||
return URL(fileURLWithPath: _temporaryFilePath(
|
||||
fileName: nil,
|
||||
fileExtension: fileExtension,
|
||||
isAvailableWhileDeviceLocked: isAvailableWhileDeviceLocked,
|
||||
))
|
||||
}
|
||||
|
||||
static func temporaryFileUrl(
|
||||
fileName: String,
|
||||
fileExtension: String?,
|
||||
isAvailableWhileDeviceLocked: Bool,
|
||||
) -> URL {
|
||||
return URL(fileURLWithPath: _temporaryFilePath(
|
||||
return URL(fileURLWithPath: temporaryFilePath(
|
||||
fileName: fileName,
|
||||
fileExtension: fileExtension,
|
||||
isAvailableWhileDeviceLocked: isAvailableWhileDeviceLocked,
|
||||
))
|
||||
}
|
||||
|
||||
static func temporaryFilePath(
|
||||
fileName: String,
|
||||
fileExtension: String?,
|
||||
public static func temporaryFilePath(
|
||||
fileName: String? = nil,
|
||||
fileExtension: String? = nil,
|
||||
isAvailableWhileDeviceLocked: Bool,
|
||||
) -> String {
|
||||
return _temporaryFilePath(
|
||||
fileName: fileName,
|
||||
fileExtension: fileExtension,
|
||||
isAvailableWhileDeviceLocked: isAvailableWhileDeviceLocked,
|
||||
)
|
||||
}
|
||||
|
||||
static func temporaryFilePath(
|
||||
fileExtension: String,
|
||||
isAvailableWhileDeviceLocked: Bool,
|
||||
) -> String {
|
||||
return _temporaryFilePath(
|
||||
fileName: nil,
|
||||
fileExtension: fileExtension,
|
||||
isAvailableWhileDeviceLocked: isAvailableWhileDeviceLocked,
|
||||
)
|
||||
}
|
||||
|
||||
private static func _temporaryFilePath(
|
||||
fileName: String?,
|
||||
fileExtension: String?,
|
||||
isAvailableWhileDeviceLocked: Bool,
|
||||
) -> String {
|
||||
let tempDirPath = tempDirPath(availableWhileDeviceLocked: isAvailableWhileDeviceLocked)
|
||||
var tempDirPath = tempDirPath(availableWhileDeviceLocked: isAvailableWhileDeviceLocked)
|
||||
if fileName != nil {
|
||||
tempDirPath = (tempDirPath as NSString).appendingPathComponent(UUID().uuidString)
|
||||
let fileProtectionType: FileProtectionType = isAvailableWhileDeviceLocked ? .completeUntilFirstUserAuthentication : .complete
|
||||
guard ensureDirectoryExists(tempDirPath, fileProtectionType: fileProtectionType) else {
|
||||
owsFail("couldn't create temporary directory")
|
||||
}
|
||||
}
|
||||
var fileName = fileName ?? UUID().uuidString
|
||||
if
|
||||
let fileExtension,
|
||||
!fileExtension.isEmpty
|
||||
{
|
||||
fileName = "\(fileName).\(fileExtension)"
|
||||
if let fileExtension, !fileExtension.isEmpty {
|
||||
fileName += "." + fileExtension
|
||||
}
|
||||
let filePath = (tempDirPath as NSString).appendingPathComponent(fileName)
|
||||
return filePath
|
||||
}
|
||||
|
||||
private static func tempDirPath(availableWhileDeviceLocked: Bool) -> String {
|
||||
return availableWhileDeviceLocked
|
||||
? OWSTemporaryDirectoryAccessibleAfterFirstAuth()
|
||||
: OWSTemporaryDirectory()
|
||||
return availableWhileDeviceLocked ? tempDirAfterFirstUnlock : tempDirComplete
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
// MARK: -
|
||||
|
||||
public extension OWSFileSystem {
|
||||
static func deleteFile(_ filePath: String, ignoreIfMissing: Bool = false) -> Bool {
|
||||
public static func deleteFile(_ filePath: String, ignoreIfMissing: Bool = false) -> Bool {
|
||||
do {
|
||||
try FileManager.default.removeItem(atPath: filePath)
|
||||
return true
|
||||
@ -432,17 +390,15 @@ public extension OWSFileSystem {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Remaining space
|
||||
// MARK: - Remaining space
|
||||
|
||||
public extension OWSFileSystem {
|
||||
/// Get the remaining free space for a path's volume in bytes.
|
||||
///
|
||||
/// See [Apple's example][0]. It checks "important" storage (versus "opportunistic" storage).
|
||||
///
|
||||
/// [0]: https://developer.apple.com/documentation/foundation/nsurlresourcekey/checking_volume_storage_capacity
|
||||
static func freeSpaceInBytes(forPath path: URL) throws -> UInt64 {
|
||||
public static func freeSpaceInBytes(forPath path: URL) throws -> UInt64 {
|
||||
let resourceValues = try path.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
|
||||
guard let result = resourceValues.volumeAvailableCapacityForImportantUsage else {
|
||||
throw OWSGenericError("Could not determine remaining disk space")
|
||||
|
||||
@ -11,7 +11,7 @@ import Testing
|
||||
struct ConnectionLockTest {
|
||||
@Test
|
||||
func testEachPriority() async throws {
|
||||
let filePath = OWSTemporaryDirectory().appendingPathComponent("\(UUID().uuidString).lock")
|
||||
let filePath = OWSFileSystem.temporaryFilePath(fileExtension: "lock", isAvailableWhileDeviceLocked: false)
|
||||
for priority in 1...3 {
|
||||
let connectionLock = ConnectionLock(filePath: filePath, priority: priority, of: 3)
|
||||
defer { connectionLock.close() }
|
||||
|
||||
@ -128,12 +128,6 @@ public struct PreviewableAttachment {
|
||||
)
|
||||
}
|
||||
|
||||
private static var videoTempPath: URL {
|
||||
let videoDir = URL(fileURLWithPath: OWSTemporaryDirectory()).appendingPathComponent("video")
|
||||
OWSFileSystem.ensureDirectoryExists(videoDir.path)
|
||||
return videoDir
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public static func compressVideoAsMp4(
|
||||
dataSource: DataSourcePath,
|
||||
@ -168,7 +162,7 @@ public struct PreviewableAttachment {
|
||||
sessionCallback(exportSession)
|
||||
}
|
||||
|
||||
let exportURL = videoTempPath.appendingPathComponent(UUID().uuidString).appendingPathExtension("mp4")
|
||||
let exportURL = OWSFileSystem.temporaryFileUrl(fileExtension: "mp4", isAvailableWhileDeviceLocked: false)
|
||||
|
||||
try await exportSession.exportAsync(to: exportURL, as: .mp4)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user