Music_Player3/Pods/Tiercel/Sources/Utility/ResumeDataHelper.swift
2024-06-03 09:48:39 +08:00

199 lines
8.5 KiB
Swift

//
// ResumeDataHelper.swift
// Tiercel
//
// Created by Daniels on 2019/1/7.
// Copyright © 2019 Daniels. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
internal enum ResumeDataHelper {
static let infoVersionKey = "NSURLSessionResumeInfoVersion"
static let currentRequestKey = "NSURLSessionResumeCurrentRequest"
static let originalRequestKey = "NSURLSessionResumeOriginalRequest"
static let resumeByteRangeKey = "NSURLSessionResumeByteRange"
static let infoTempFileNameKey = "NSURLSessionResumeInfoTempFileName"
static let infoLocalPathKey = "NSURLSessionResumeInfoLocalPath"
static let bytesReceivedKey = "NSURLSessionResumeBytesReceived"
static let archiveRootObjectKey = "NSKeyedArchiveRootObjectKey"
internal static func handleResumeData(_ data: Data) -> Data? {
if #available(iOS 11.3, *) {
return data
} else if #available(iOS 11.0, *) {
// 11.0 - 11.2 bug
return deleteResumeByteRange(data)
} else if #available(iOS 10.2, *) {
return data
} else if #available(iOS 10.0, *) {
// 10.0 - 10.1 bug
return correctResumeData(data)
} else {
return data
}
}
/// 11.0 - 11.2 resumeData bug
///
/// - Parameter data:
/// - Returns:
private static func deleteResumeByteRange(_ data: Data) -> Data? {
guard let resumeDictionary = getResumeDictionary(data) else { return nil }
resumeDictionary.removeObject(forKey: resumeByteRangeKey)
return try? PropertyListSerialization.data(fromPropertyList: resumeDictionary,
format: PropertyListSerialization.PropertyListFormat.xml,
options: PropertyListSerialization.WriteOptions())
}
/// 10.0 - 10.1 resumeData bug
///
/// - Parameter data:
/// - Returns:
private static func correctResumeData(_ data: Data) -> Data? {
guard let resumeDictionary = getResumeDictionary(data) else { return nil }
if let currentRequest = resumeDictionary[currentRequestKey] as? Data {
resumeDictionary[currentRequestKey] = correct(with: currentRequest)
}
if let originalRequest = resumeDictionary[originalRequestKey] as? Data {
resumeDictionary[originalRequestKey] = correct(with: originalRequest)
}
return try? PropertyListSerialization.data(fromPropertyList: resumeDictionary,
format: PropertyListSerialization.PropertyListFormat.xml,
options: PropertyListSerialization.WriteOptions())
}
/// resumeData
///
/// - Parameter data:
/// - Returns:
internal static func getResumeDictionary(_ data: Data) -> NSMutableDictionary? {
// In beta versions, resumeData is NSKeyedArchive encoded instead of plist
var object: NSDictionary?
if #available(OSX 10.11, iOS 9.0, *) {
let keyedUnarchiver = NSKeyedUnarchiver(forReadingWith: data)
do {
object = try keyedUnarchiver.decodeTopLevelObject(of: NSDictionary.self, forKey: archiveRootObjectKey)
if object == nil {
object = try keyedUnarchiver.decodeTopLevelObject(of: NSDictionary.self, forKey: NSKeyedArchiveRootObjectKey)
}
} catch {}
keyedUnarchiver.finishDecoding()
}
if object == nil {
do {
object = try PropertyListSerialization.propertyList(from: data,
options: .mutableContainersAndLeaves,
format: nil) as? NSDictionary
} catch {}
}
if let resumeDictionary = object as? NSMutableDictionary {
return resumeDictionary
}
guard let resumeDictionary = object else { return nil }
return NSMutableDictionary(dictionary: resumeDictionary)
}
internal static func getTmpFileName(_ data: Data) -> String? {
guard let resumeDictionary = ResumeDataHelper.getResumeDictionary(data),
let version = resumeDictionary[infoVersionKey] as? Int
else { return nil }
if version > 1 {
return resumeDictionary[infoTempFileNameKey] as? String
} else {
guard let path = resumeDictionary[infoLocalPathKey] as? String else { return nil }
let url = URL(fileURLWithPath: path)
return url.lastPathComponent
}
}
/// resumeData
///
/// - Parameter data:
/// - Returns:
private static func correct(with data: Data) -> Data? {
if NSKeyedUnarchiver.unarchiveObject(with: data) != nil {
return data
}
guard let resumeDictionary = try? PropertyListSerialization.propertyList(from: data,
options: .mutableContainersAndLeaves,
format: nil) as? NSMutableDictionary
else { return nil }
// Rectify weird __nsurlrequest_proto_props objects to $number pattern
var k = 0
while ((resumeDictionary["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "$\(k)") != nil {
k += 1
}
var i = 0
while ((resumeDictionary["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_prop_obj_\(i)") != nil {
let arr = resumeDictionary["$objects"] as? NSMutableArray
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_prop_obj_\(i)"] {
dic.setObject(obj, forKey: "$\(i + k)" as NSString)
dic.removeObject(forKey: "__nsurlrequest_proto_prop_obj_\(i)")
arr?[1] = dic
resumeDictionary["$objects"] = arr
}
i += 1
}
if ((resumeDictionary["$objects"] as? NSArray)?[1] as? NSDictionary)?.object(forKey: "__nsurlrequest_proto_props") != nil {
let arr = resumeDictionary["$objects"] as? NSMutableArray
if let dic = arr?[1] as? NSMutableDictionary, let obj = dic["__nsurlrequest_proto_props"] {
dic.setObject(obj, forKey: "$\(i + k)" as NSString)
dic.removeObject(forKey: "__nsurlrequest_proto_props")
arr?[1] = dic
resumeDictionary["$objects"] = arr
}
}
if let obj = (resumeDictionary["$top"] as? NSMutableDictionary)?.object(forKey: archiveRootObjectKey) as AnyObject? {
(resumeDictionary["$top"] as? NSMutableDictionary)?.setObject(obj, forKey: NSKeyedArchiveRootObjectKey as NSString)
(resumeDictionary["$top"] as? NSMutableDictionary)?.removeObject(forKey: archiveRootObjectKey)
}
// Reencode archived object
return try? PropertyListSerialization.data(fromPropertyList: resumeDictionary,
format: PropertyListSerialization.PropertyListFormat.binary,
options: PropertyListSerialization.WriteOptions())
}
}