678 lines
24 KiB
Swift
678 lines
24 KiB
Swift
//
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Foundation
|
|
import LibSignalClient
|
|
|
|
public enum OWSRequestFactory {
|
|
|
|
static let textSecureAccountsAPI = "v1/accounts"
|
|
static let textSecureAttributesAPI = "v1/accounts/attributes/"
|
|
static let textSecureMessagesAPI = "v1/messages/"
|
|
static let textSecureMultiRecipientMessageAPI = "v1/messages/multi_recipient"
|
|
static let textSecureKeysAPI = "v2/keys"
|
|
static let textSecureSignedKeysAPI = "v2/keys/signed"
|
|
static let textSecureDirectoryAPI = "v1/directory"
|
|
static let textSecure2FAAPI = "v1/accounts/pin"
|
|
static let textSecureRegistrationLockV2API = "v1/accounts/registration_lock"
|
|
static let textSecureGiftBadgePricesAPI = "v1/subscription/boost/amounts/gift"
|
|
|
|
static let textSecureHTTPTimeOut: TimeInterval = 10
|
|
|
|
static func useUDAuth(request: TSRequest, accessKey: SMKUDAccessKey) {
|
|
// Suppress normal auth headers.
|
|
request.shouldHaveAuthorizationHeaders = false
|
|
|
|
// Add UD auth header.
|
|
request.setValue(accessKey.keyData.base64EncodedString(), forHTTPHeaderField: "Unidentified-Access-Key")
|
|
|
|
request.isUDRequest = true
|
|
}
|
|
|
|
// MARK: - Other
|
|
|
|
static func allocAttachmentRequestV4() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v4/attachments/form/upload")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func currencyConversionRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v1/payments/conversions")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func getRemoteConfigRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "/v1/config/")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
public static func turnServerInfoRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v1/calling/relays")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
// MARK: - Auth
|
|
|
|
static func authCredentialRequest(from fromRedemptionSeconds: UInt64, to toRedemptionSeconds: UInt64) -> TSRequest {
|
|
owsAssertDebug(fromRedemptionSeconds > 0)
|
|
owsAssertDebug(toRedemptionSeconds > 0)
|
|
|
|
let path = "v1/certificate/auth/group?redemptionStartSeconds=\(fromRedemptionSeconds)&redemptionEndSeconds=\(toRedemptionSeconds)&zkcCredential=true"
|
|
return TSRequest(url: URL(string: path)!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
public static func paymentsAuthenticationCredentialRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "/v1/payments/auth")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func remoteAttestationAuthRequestForCDSI() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v2/directory/auth")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func remoteAttestationAuthRequestForKeyBackup() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v1/backup/auth")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func remoteAttestationAuthRequestForSVR2() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v2/backup/auth")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func storageAuthRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v1/storage/auth")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
// MARK: - Challenges
|
|
|
|
static func pushChallengeRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "/v1/challenge/push")!, method: "POST", parameters: [:])
|
|
}
|
|
|
|
static func pushChallengeResponse(token: String) -> TSRequest {
|
|
return TSRequest(url: URL(string: "/v1/challenge")!, method: "PUT", parameters: ["type": "rateLimitPushChallenge", "challenge": token])
|
|
}
|
|
|
|
static func recaptchChallengeResponse(serverToken: String, captchaToken: String) -> TSRequest {
|
|
return TSRequest(url: URL(string: "/v1/challenge")!, method: "PUT", parameters: ["type": "captcha", "token": serverToken, "captcha": captchaToken])
|
|
}
|
|
|
|
// MARK: - Messages
|
|
|
|
static func getMessagesRequest() -> TSRequest {
|
|
let request = TSRequest(url: URL(string: "v1/messages")!, method: "GET", parameters: [:])
|
|
StoryManager.appendStoryHeaders(to: request)
|
|
request.shouldCheckDeregisteredOn401 = true
|
|
return request
|
|
}
|
|
|
|
static func acknowledgeMessageDeliveryRequest(serverGuid: String) -> TSRequest {
|
|
owsAssertDebug(!serverGuid.isEmpty)
|
|
|
|
let path = "v1/messages/uuid/\(serverGuid)"
|
|
|
|
return TSRequest(url: URL(string: path)!, method: "DELETE", parameters: [:])
|
|
}
|
|
|
|
static func udSenderCertificateRequest(uuidOnly: Bool) -> TSRequest {
|
|
var path = "v1/certificate/delivery"
|
|
if uuidOnly {
|
|
path += "?includeE164=false"
|
|
}
|
|
return TSRequest(url: URL(string: path)!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func submitMessageRequest(
|
|
serviceId: ServiceId,
|
|
messages: [DeviceMessage],
|
|
timestamp: UInt64,
|
|
udAccessKey: SMKUDAccessKey?,
|
|
isOnline: Bool,
|
|
isUrgent: Bool,
|
|
isStory: Bool
|
|
) -> TSRequest {
|
|
// NOTE: messages may be empty; See comments in OWSDeviceManager.
|
|
owsAssertDebug(timestamp > 0)
|
|
|
|
let path = "\(self.textSecureMessagesAPI)\(serviceId.serviceIdString)?story=\(isStory ? "true" : "false")"
|
|
|
|
// Returns the per-account-message parameters used when submitting a message to
|
|
// the Signal Web Service.
|
|
// See
|
|
// <https://github.com/signalapp/Signal-Server/blob/65da844d70369cb8b44966cfb2d2eb9b925a6ba4/service/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingMessageList.java>.
|
|
let parameters: [String: Any] = [
|
|
"messages": messages.map { $0.requestParameters() },
|
|
"timestamp": timestamp,
|
|
"online": isOnline,
|
|
"urgent": isUrgent
|
|
]
|
|
|
|
let request = TSRequest(url: URL(string: path)!, method: "PUT", parameters: parameters)
|
|
if let udAccessKey {
|
|
useUDAuth(request: request, accessKey: udAccessKey)
|
|
}
|
|
return request
|
|
}
|
|
|
|
static func submitMultiRecipientMessageRequest(ciphertext: Data, accessKey: SMKUDAccessKey, timestamp: UInt64, isOnline: Bool, isUrgent: Bool, isStory: Bool) -> TSRequest {
|
|
owsAssertDebug(timestamp > 0)
|
|
|
|
// We build the URL by hand instead of passing the query parameters into the query parameters
|
|
// AFNetworking won't handle both query parameters and an httpBody (which we need here)
|
|
var components = URLComponents(string: self.textSecureMultiRecipientMessageAPI)!
|
|
components.queryItems = [
|
|
URLQueryItem(name: "ts", value: "\(timestamp)"),
|
|
URLQueryItem(name: "online", value: isOnline ? "true" : "false"),
|
|
URLQueryItem(name: "urgent", value: isUrgent ? "true" : "false"),
|
|
URLQueryItem(name: "story", value: isStory ? "true" : "false"),
|
|
]
|
|
|
|
let request = TSRequest(url: components.url!, method: "PUT", parameters: nil)
|
|
request.setValue("application/vnd.signal-messenger.mrm", forHTTPHeaderField: "Content-Type")
|
|
useUDAuth(request: request, accessKey: accessKey)
|
|
request.httpBody = ciphertext
|
|
return request
|
|
}
|
|
|
|
// MARK: - Registration
|
|
|
|
static func disable2FARequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: self.textSecure2FAAPI)!, method: "DELETE", parameters: [:])
|
|
}
|
|
|
|
public static func enableRegistrationLockV2Request(token: String) -> TSRequest {
|
|
owsAssertDebug(nil != token.nilIfEmpty)
|
|
|
|
let url = URL(string: textSecureRegistrationLockV2API)!
|
|
return TSRequest(url: url,
|
|
method: HTTPMethod.put.methodName,
|
|
parameters: [
|
|
"registrationLock": token
|
|
])
|
|
}
|
|
|
|
static func disableRegistrationLockV2Request() -> TSRequest {
|
|
let url = URL(string: textSecureRegistrationLockV2API)!
|
|
return TSRequest(url: url,
|
|
method: HTTPMethod.delete.methodName,
|
|
parameters: [:])
|
|
}
|
|
|
|
public static func registerForPushRequest(apnsToken: String) -> TSRequest {
|
|
owsAssertDebug(!apnsToken.isEmpty)
|
|
|
|
let path = "\(self.textSecureAccountsAPI)/apn"
|
|
|
|
return TSRequest(url: URL(string: path)!, method: "PUT", parameters: ["apnRegistrationId": apnsToken])
|
|
}
|
|
|
|
static func unregisterAccountRequest() -> TSRequest {
|
|
let path = "\(self.textSecureAccountsAPI)/me"
|
|
return TSRequest(url: URL(string: path)!, method: "DELETE", parameters: [:])
|
|
}
|
|
|
|
static let batchIdentityCheckElementsLimit = 1000
|
|
static func batchIdentityCheckRequest(elements: [[String: String]]) -> TSRequest {
|
|
precondition(elements.count <= batchIdentityCheckElementsLimit)
|
|
return .init(url: .init(string: "v1/profile/identity_check/batch")!, method: HTTPMethod.post.methodName, parameters: ["elements": elements])
|
|
}
|
|
|
|
// MARK: - Devices
|
|
|
|
static func getDevicesRequest() -> TSRequest {
|
|
return TSRequest(url: URL(string: "v1/devices/")!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func deviceProvisioningCode() -> TSRequest {
|
|
return TSRequest(
|
|
url: URL(string: "v1/devices/provisioning/code")!,
|
|
method: "GET",
|
|
parameters: nil
|
|
)
|
|
}
|
|
|
|
static func provisionDevice(withMessageBody messageBody: Data, ephemeralDeviceId: String) -> TSRequest {
|
|
owsAssertDebug(!messageBody.isEmpty)
|
|
owsAssertDebug(!ephemeralDeviceId.isEmpty)
|
|
|
|
return .init(
|
|
url: .init(pathComponents: ["v1", "provisioning", ephemeralDeviceId])!,
|
|
method: "PUT",
|
|
parameters: ["body": messageBody.base64EncodedString()]
|
|
)
|
|
}
|
|
|
|
static func deleteDeviceRequest(
|
|
_ device: OWSDevice
|
|
) -> TSRequest {
|
|
return TSRequest(
|
|
url: URL(string: "/v1/devices/\(device.deviceId)")!,
|
|
method: HTTPMethod.delete.methodName,
|
|
parameters: nil
|
|
)
|
|
}
|
|
|
|
// MARK: - Donations
|
|
|
|
static func donationConfiguration() -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(string: "v1/subscription/configuration")!,
|
|
method: "GET",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
return result
|
|
}
|
|
|
|
static func setSubscriberID(_ subscriberID: Data) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: ["v1", "subscription", subscriberID.asBase64Url])!,
|
|
method: "PUT",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func deleteSubscriberID(_ subscriberID: Data) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: ["v1", "subscription", subscriberID.asBase64Url])!,
|
|
method: "DELETE",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionSetDefaultPaymentMethod(
|
|
subscriberId: Data,
|
|
processor: String,
|
|
paymentMethodId: String
|
|
) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberId.asBase64Url,
|
|
"default_payment_method",
|
|
processor,
|
|
paymentMethodId
|
|
])!,
|
|
method: "POST",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionSetDefaultIDEALPaymentMethod(
|
|
subscriberId: Data,
|
|
setupIntentId: String
|
|
) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberId.asBase64Url,
|
|
"default_payment_method_for_ideal",
|
|
setupIntentId
|
|
])!,
|
|
method: "POST",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionGetCurrentSubscriptionLevelRequest(subscriberID: Data) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberID.asBase64Url,
|
|
])!,
|
|
method: "GET",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionCreateStripePaymentMethodRequest(subscriberID: Data) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberID.asBase64Url,
|
|
"create_payment_method",
|
|
])!,
|
|
method: "POST",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionCreatePaypalPaymentMethodRequest(
|
|
subscriberID: Data,
|
|
returnURL: URL,
|
|
cancelURL: URL
|
|
) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberID.asBase64Url,
|
|
"create_payment_method",
|
|
"paypal",
|
|
])!,
|
|
method: "POST",
|
|
parameters: [
|
|
"returnUrl": returnURL.absoluteString,
|
|
"cancelUrl": cancelURL.absoluteString,
|
|
]
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionSetSubscriptionLevelRequest(
|
|
subscriberID: Data,
|
|
level: UInt,
|
|
currency: String,
|
|
idempotencyKey: String
|
|
) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberID.asBase64Url,
|
|
"level",
|
|
String(level),
|
|
currency,
|
|
idempotencyKey,
|
|
])!,
|
|
method: "PUT",
|
|
parameters: nil
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionReceiptCredentialsRequest(
|
|
subscriberID: Data,
|
|
request: Data
|
|
) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
subscriberID.asBase64Url,
|
|
"receipt_credentials",
|
|
])!,
|
|
method: "POST",
|
|
parameters: [
|
|
"receiptCredentialRequest": request.base64EncodedString(),
|
|
]
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
result.applyRedactionStrategy(.redactURLForSuccessResponses())
|
|
return result
|
|
}
|
|
|
|
static func subscriptionRedeemReceiptCredential(
|
|
receiptCredentialPresentation: Data
|
|
) -> TSRequest {
|
|
return TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"donation",
|
|
"redeem-receipt",
|
|
])!,
|
|
method: "POST",
|
|
parameters: [
|
|
"receiptCredentialPresentation": receiptCredentialPresentation.base64EncodedString(),
|
|
"visible": NSObject.subscriptionManager.displayBadgesOnProfile,
|
|
"primary": false,
|
|
]
|
|
)
|
|
}
|
|
|
|
static func boostReceiptCredentials(
|
|
with paymentIntentID: String,
|
|
for paymentProcessor: String,
|
|
request: Data
|
|
) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
"boost",
|
|
"receipt_credentials",
|
|
])!,
|
|
method: "POST",
|
|
parameters: [
|
|
"paymentIntentId": paymentIntentID,
|
|
"receiptCredentialRequest": request.base64EncodedString(),
|
|
"processor": paymentProcessor,
|
|
]
|
|
)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
return result
|
|
}
|
|
|
|
public static func bankMandateRequest(bankTransferType: StripePaymentMethod.BankTransfer) -> TSRequest {
|
|
let result = TSRequest(
|
|
url: .init(pathComponents: [
|
|
"v1",
|
|
"subscription",
|
|
"bank_mandate",
|
|
bankTransferType.rawValue,
|
|
])!,
|
|
method: "GET",
|
|
parameters: nil
|
|
)
|
|
result.addValue(OWSHttpHeaders.acceptLanguageHeaderValue, forHTTPHeaderField: OWSHttpHeaders.acceptLanguageHeaderKey)
|
|
result.shouldHaveAuthorizationHeaders = false
|
|
return result
|
|
}
|
|
|
|
// MARK: - Keys
|
|
|
|
static func preKeyRequestParameters(_ preKeyRecord: SignalServiceKit.PreKeyRecord) -> [String: Any] {
|
|
[
|
|
"keyId": preKeyRecord.id,
|
|
"publicKey": preKeyRecord.keyPair.keyPair.publicKey.serialize().asData.base64EncodedStringWithoutPadding()
|
|
]
|
|
}
|
|
|
|
static func signedPreKeyRequestParameters(_ signedPreKeyRecord: SignalServiceKit.SignedPreKeyRecord) -> [String: Any] {
|
|
[
|
|
"keyId": signedPreKeyRecord.id,
|
|
"publicKey": signedPreKeyRecord.keyPair.keyPair.publicKey.serialize().asData.base64EncodedStringWithoutPadding(),
|
|
"signature": signedPreKeyRecord.signature.base64EncodedStringWithoutPadding()
|
|
]
|
|
}
|
|
|
|
static func pqPreKeyRequestParameters(_ pqPreKeyRecord: KyberPreKeyRecord) -> [String: Any] {
|
|
[
|
|
"keyId": pqPreKeyRecord.id,
|
|
"publicKey": pqPreKeyRecord.keyPair.publicKey.serialize().asData.base64EncodedStringWithoutPadding(),
|
|
"signature": pqPreKeyRecord.signature.base64EncodedStringWithoutPadding()
|
|
]
|
|
}
|
|
|
|
static func availablePreKeysCountRequest(for identity: OWSIdentity) -> TSRequest {
|
|
var path = self.textSecureKeysAPI
|
|
if let queryParam = queryParam(for: identity) {
|
|
path += "?" + queryParam
|
|
}
|
|
return TSRequest(url: URL(string: path)!, method: "GET", parameters: [:])
|
|
}
|
|
|
|
static func recipientPreKeyRequest(serviceId: ServiceId, deviceId: UInt32, udAccessKey: SMKUDAccessKey?) -> TSRequest {
|
|
let path = "\(self.textSecureKeysAPI)/\(serviceId.serviceIdString)/\(deviceId)"
|
|
|
|
let request = TSRequest(url: URL(string: path)!, method: "GET", parameters: [:])
|
|
if let udAccessKey {
|
|
useUDAuth(request: request, accessKey: udAccessKey)
|
|
}
|
|
return request
|
|
}
|
|
|
|
static func registerSignedPrekeyRequest(for identity: OWSIdentity, signedPreKey: SignalServiceKit.SignedPreKeyRecord) -> TSRequest {
|
|
var path = self.textSecureSignedKeysAPI
|
|
if let queryParam = queryParam(for: identity) {
|
|
path += "?" + queryParam
|
|
}
|
|
return TSRequest(url: URL(string: path)!, method: "PUT", parameters: signedPreKeyRequestParameters(signedPreKey))
|
|
}
|
|
|
|
/// If a username and password are both provided, those are used for the request's
|
|
/// Authentication header. Otherwise, the default header is used (whatever's on
|
|
/// TSAccountManager).
|
|
static func registerPrekeysRequest(
|
|
identity: OWSIdentity,
|
|
signedPreKeyRecord: SignalServiceKit.SignedPreKeyRecord?,
|
|
prekeyRecords: [SignalServiceKit.PreKeyRecord]?,
|
|
pqLastResortPreKeyRecord: KyberPreKeyRecord?,
|
|
pqPreKeyRecords: [KyberPreKeyRecord]?,
|
|
auth: ChatServiceAuth
|
|
) -> TSRequest {
|
|
var path = textSecureKeysAPI
|
|
if let queryParam = queryParam(for: identity) {
|
|
path = path.appending("?\(queryParam)")
|
|
}
|
|
|
|
var parameters = [String: Any]()
|
|
|
|
if let signedPreKeyRecord {
|
|
parameters["signedPreKey"] = signedPreKeyRequestParameters(signedPreKeyRecord)
|
|
}
|
|
if let prekeyRecords {
|
|
parameters["preKeys"] = prekeyRecords.map { self.preKeyRequestParameters($0) }
|
|
}
|
|
if let pqLastResortPreKeyRecord {
|
|
parameters["pqLastResortPreKey"] = pqPreKeyRequestParameters(pqLastResortPreKeyRecord)
|
|
}
|
|
if let pqPreKeyRecords {
|
|
parameters["pqPreKeys"] = pqPreKeyRecords.map { self.pqPreKeyRequestParameters($0) }
|
|
}
|
|
|
|
let request = TSRequest(
|
|
url: URL(string: path)!,
|
|
method: "PUT",
|
|
parameters: parameters
|
|
)
|
|
request.setAuth(auth)
|
|
return request
|
|
}
|
|
|
|
static func queryParam(for identity: OWSIdentity) -> String? {
|
|
switch identity {
|
|
case .aci:
|
|
return nil
|
|
case .pni:
|
|
return "identity=pni"
|
|
}
|
|
}
|
|
|
|
// MARK: - Profiles
|
|
|
|
static func getUnversionedProfileRequest(serviceId: ServiceId, udAccessKey: SMKUDAccessKey?, auth: ChatServiceAuth) -> TSRequest {
|
|
let path = "v1/profile/\(serviceId.serviceIdString)"
|
|
let request = TSRequest(url: URL(string: path)!, method: "GET", parameters: [:])
|
|
if let udAccessKey {
|
|
useUDAuth(request: request, accessKey: udAccessKey)
|
|
} else {
|
|
request.setAuth(auth)
|
|
}
|
|
return request
|
|
}
|
|
|
|
static func getVersionedProfileRequest(
|
|
aci: Aci,
|
|
profileKeyVersion: String?,
|
|
credentialRequest: Data?,
|
|
udAccessKey: SMKUDAccessKey?,
|
|
auth: ChatServiceAuth
|
|
) -> TSRequest {
|
|
var components = [String]()
|
|
components.append(aci.serviceIdString)
|
|
if let profileKeyVersion, !profileKeyVersion.isEmpty {
|
|
components.append(profileKeyVersion.lowercased())
|
|
if let credentialRequest, !credentialRequest.isEmpty {
|
|
components.append(credentialRequest.hexadecimalString + "?credentialType=expiringProfileKey")
|
|
}
|
|
}
|
|
|
|
let path = "v1/profile/\(components.joined(separator: "/"))"
|
|
|
|
let request = TSRequest(url: URL(string: path)!, method: "GET", parameters: [:])
|
|
if let udAccessKey {
|
|
useUDAuth(request: request, accessKey: udAccessKey)
|
|
} else {
|
|
request.setAuth(auth)
|
|
}
|
|
return request
|
|
}
|
|
|
|
public static func setVersionedProfileRequest(
|
|
name: ProfileValue?,
|
|
bio: ProfileValue?,
|
|
bioEmoji: ProfileValue?,
|
|
hasAvatar: Bool,
|
|
sameAvatar: Bool,
|
|
paymentAddress: ProfileValue?,
|
|
phoneNumberSharing: ProfileValue,
|
|
visibleBadgeIds: [String],
|
|
version: String,
|
|
commitment: Data,
|
|
auth: ChatServiceAuth
|
|
) -> TSRequest {
|
|
var parameters: [String: Any] = [
|
|
"avatar": hasAvatar,
|
|
"sameAvatar": sameAvatar,
|
|
"badgeIds": visibleBadgeIds,
|
|
"commitment": commitment.base64EncodedString(),
|
|
"phoneNumberSharing": phoneNumberSharing.encryptedBase64Value,
|
|
"version": version,
|
|
]
|
|
if let name {
|
|
parameters["name"] = name.encryptedBase64Value
|
|
}
|
|
if let bio {
|
|
parameters["about"] = bio.encryptedBase64Value
|
|
}
|
|
if let bioEmoji {
|
|
parameters["aboutEmoji"] = bioEmoji.encryptedBase64Value
|
|
}
|
|
if let paymentAddress {
|
|
parameters["paymentAddress"] = paymentAddress.encryptedBase64Value
|
|
}
|
|
let request = TSRequest(url: URL(string: "v1/profile/")!, method: "PUT", parameters: parameters)
|
|
request.setAuth(auth)
|
|
return request
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
extension DeviceMessage {
|
|
/// Returns the per-device-message parameters when sending a message.
|
|
///
|
|
/// See <https://github.com/signalapp/Signal-Server/blob/ab26a65/service/src/main/java/org/whispersystems/textsecuregcm/entities/IncomingMessage.java>.
|
|
func requestParameters() -> NSDictionary {
|
|
return [
|
|
"type": type.rawValue,
|
|
"destinationDeviceId": destinationDeviceId,
|
|
"destinationRegistrationId": Int32(bitPattern: destinationRegistrationId),
|
|
"content": serializedMessage.base64EncodedString()
|
|
]
|
|
}
|
|
}
|