TM-SGNL-iOS/SignalServiceKit/Upload/UploadV2.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

110 lines
4.1 KiB
Swift

//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
extension Upload {
public struct CDN0 {
public struct Form: Codable {
let acl: String
let key: String
let policy: String
let algorithm: String
let credential: String
let date: String
let signature: String
let attachmentId: UInt64?
let attachmentIdString: String?
}
}
}
extension Upload.CDN0.Form {
public enum ParsingError: Error {
case missingField(String)
}
public static func parse(proto: GroupsProtoAvatarUploadAttributes) throws -> Self {
guard let acl = proto.acl else { throw ParsingError.missingField("acl") }
guard let key = proto.key else { throw ParsingError.missingField("key") }
guard let policy = proto.policy else { throw ParsingError.missingField("policy") }
guard let algorithm = proto.algorithm else { throw ParsingError.missingField("algorithm") }
guard let credential = proto.credential else { throw ParsingError.missingField("credential") }
guard let date = proto.date else { throw ParsingError.missingField("date") }
guard let signature = proto.signature else { throw ParsingError.missingField("signature") }
return .init(
acl: acl,
key: key,
policy: policy,
algorithm: algorithm,
credential: credential,
date: date,
signature: signature,
attachmentId: nil,
attachmentIdString: nil
)
}
}
extension Upload.CDN0 {
public static func upload(data: Data, uploadForm: Upload.CDN0.Form) async throws -> String {
if DependenciesBridge.shared.appExpiry.isExpired {
throw OWSAssertionError("App is expired.")
}
let dataFileUrl = OWSFileSystem.temporaryFileUrl(isAvailableWhileDeviceLocked: true)
try data.write(to: dataFileUrl)
let cdn0UrlSession = SSKEnvironment.shared.signalServiceRef.urlSessionForCdn(cdnNumber: 0, maxResponseSize: nil)
// urlPath is "" for all endpoints that still use CDN0
let request = try cdn0UrlSession.endpoint.buildRequest("", method: .post)
// We have to build up the form manually vs. simply passing in a parameters dict
// because AWS is sensitive to the order of the form params (at least the "key"
// field must occur early on).
//
// For consistency, all fields are ordered here in a known working order.
var textParts = uploadForm.asOrderedDictionary
textParts.append(key: "Content-Type", value: MimeType.applicationOctetStream.rawValue)
do {
_ = try await cdn0UrlSession.performMultiPartUpload(
request: request,
fileUrl: dataFileUrl,
name: "file",
fileName: "file",
mimeType: MimeType.applicationOctetStream.rawValue,
textParts: textParts
)
} catch {
Logger.warn("\(error)")
throw error
}
return uploadForm.key
}
}
// See: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
fileprivate extension Upload.CDN0.Form {
var asOrderedDictionary: OrderedDictionary<String, String> {
// We have to build up the form manually vs. simply passing in a parameters dict
// because AWS is sensitive to the order of the form params (at least the "key"
// field must occur early on).
var result = OrderedDictionary<String, String>()
// For consistency, all fields are ordered here in a known working order.
result.append(key: "key", value: self.key)
result.append(key: "acl", value: self.acl)
result.append(key: "x-amz-algorithm", value: self.algorithm)
result.append(key: "x-amz-credential", value: self.credential)
result.append(key: "x-amz-date", value: self.date)
result.append(key: "policy", value: self.policy)
result.append(key: "x-amz-signature", value: self.signature)
return result
}
}