// // Copyright 2019 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation class DownloadStickerOperation: CDNDownloadOperation { // MARK: - Cache private static let cache = LRUCache(maxSize: 256) public class func cachedUrl(for stickerInfo: StickerInfo) -> URL? { guard let stickerUrl = cache.object(forKey: stickerInfo.asKey()) else { return nil } guard OWSFileSystem.fileOrFolderExists(url: stickerUrl) else { return nil } return stickerUrl } private class func setCachedUrl(_ url: URL, for stickerInfo: StickerInfo) { cache.setObject(url, forKey: stickerInfo.asKey()) } // MARK: - private let stickerInfo: StickerInfo private let success: (URL) -> Void private let failure: (Error) -> Void @objc public init( stickerInfo: StickerInfo, success: @escaping (URL) -> Void, failure: @escaping (Error) -> Void ) { assert(stickerInfo.packId.count > 0) assert(stickerInfo.packKey.count > 0) self.stickerInfo = stickerInfo self.success = success self.failure = failure super.init() } override public func run() { if let stickerUrl = DownloadStickerOperation.cachedUrl(for: stickerInfo) { success(stickerUrl) self.reportSuccess() return } if let stickerUrl = loadInstalledStickerUrl() { success(stickerUrl) self.reportSuccess() return } // https://cdn.signal.org/stickers//full/ let urlPath = "stickers/\(stickerInfo.packId.hexadecimalString)/full/\(stickerInfo.stickerId)" firstly { return try tryToDownload(urlPath: urlPath, maxDownloadSize: kMaxStickerDataDownloadSize) }.done(on: DispatchQueue.global()) { [weak self] (url: URL) in guard let self = self else { return } do { let url = try StickerManager.decrypt(at: url, packKey: self.stickerInfo.packKey) DownloadStickerOperation.setCachedUrl(url, for: self.stickerInfo) self.success(url) self.reportSuccess() } catch { owsFailDebug("Decryption failed: \(error)") self.markUrlPathAsCorrupt(urlPath) // Fail immediately; do not retry. return self.reportError(SSKUnretryableError.stickerDecryptionFailure) } }.catch(on: DispatchQueue.global()) { [weak self] error in guard let self = self else { return } return self.reportError(withUndefinedRetry: error) } } private func loadInstalledStickerUrl() -> URL? { return StickerManager.stickerDataUrlWithSneakyTransaction(stickerInfo: stickerInfo, verifyExists: true) } override public func didFail(error: Error) { Logger.error("Download exhausted retries: \(error)") failure(error) } }