mirror of
https://github.com/xtool-org/xtool.git
synced 2026-11-09 17:07:42 +03:00
2afd5307a4
## What does this PR do? Simplifies the Anisette handling code. Removes some unnecessary fields (including hard-coded Mac serial numbers, cf #348) from our http requests amongst other things. ## How was it tested? Tested on macOS: - [x] Can log in after resetting provisioning - [x] Can run `xtool ds teams` - [x] Can run `xtool ds certificates` ## AI tool usage How much of this PR was AI-assisted? (check one) - [x] **0** - No AI was used to write code - [ ] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * Anisette-backed requests now use device information provided with Anisette data, including an Android 14 device identity. * Developer service requests no longer include client-information and device-ID headers. * The Anisette command now displays fetched details in sorted order and uses the configured Anisette provider instead of prompting for provisioning data. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
112 lines
4.1 KiB
Swift
112 lines
4.1 KiB
Swift
//
|
|
// DeveloperServicesClient.swift
|
|
// Supercharge
|
|
//
|
|
// Created by Kabir Oberai on 24/07/19.
|
|
// Copyright © 2019 Kabir Oberai. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Dependencies
|
|
|
|
public struct DeveloperServicesClient: Sendable {
|
|
|
|
public enum Error: LocalizedError {
|
|
case noData
|
|
case malformedRequest
|
|
case apiError(code: Int, reason: String?)
|
|
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case .noData:
|
|
return NSLocalizedString(
|
|
"developer_services_client.error.no_data", value: "No data", comment: ""
|
|
)
|
|
case .malformedRequest:
|
|
return NSLocalizedString(
|
|
"developer_services_client.error.malformed_request", value: "Malformed request", comment: ""
|
|
)
|
|
case .apiError(let code, let reason?):
|
|
return reason.withCString { reasonC in
|
|
"\(code)".withCString { codeC in
|
|
String.localizedStringWithFormat(
|
|
NSLocalizedString(
|
|
"developer_services_client.error.sign_in_error.reason_and_code",
|
|
value: "%s (%s)",
|
|
comment: "First reason, then code"
|
|
),
|
|
reasonC, codeC
|
|
)
|
|
}
|
|
}
|
|
case .apiError(let code, _):
|
|
return "\(code)".withCString {
|
|
String.localizedStringWithFormat(
|
|
NSLocalizedString(
|
|
"developer_services_client.error.sign_in_error.only_code",
|
|
value: "An unknown error occurred (%s)",
|
|
comment: ""
|
|
),
|
|
$0
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Dependency(\.anisetteDataProvider) var anisetteDataProvider
|
|
@Dependency(\.httpClient) var httpClient
|
|
|
|
public let loginToken: DeveloperServicesLoginToken
|
|
|
|
public init(loginToken: DeveloperServicesLoginToken) {
|
|
self.loginToken = loginToken
|
|
}
|
|
|
|
public init(authData: XcodeAuthData) {
|
|
self.loginToken = authData.loginToken
|
|
}
|
|
|
|
private func send<R: DeveloperServicesRequest>(
|
|
_ request: R,
|
|
anisetteData: AnisetteData
|
|
) async throws -> R.Value {
|
|
guard let url = request.apiVersion.url(forAction: request.action) else {
|
|
throw Error.malformedRequest
|
|
}
|
|
|
|
var httpRequest = HTTPRequest(method: .post, url: url)
|
|
let acceptedLanguages = Locale.preferredLanguages.joined(separator: ", ")
|
|
|
|
httpRequest.headerFields[.acceptLanguage] = acceptedLanguages
|
|
httpRequest.headerFields[.accept] = request.apiVersion.accept
|
|
httpRequest.headerFields[.contentType] = request.apiVersion.contentType
|
|
httpRequest.headerFields[.userAgent] = "Xcode"
|
|
request.methodOverride.map { httpRequest.headerFields[.init("X-HTTP-Method-Override")!] = $0 }
|
|
|
|
httpRequest.headerFields[.init(DeviceInfo.xcodeVersionKey)!] = DeviceInfo.xcodeVersion
|
|
|
|
httpRequest.headerFields[.init("X-Apple-I-Identity-Id")!] = loginToken.adsid
|
|
httpRequest.headerFields[.init("X-Apple-App-Info")!] = AppTokenKey.xcode.rawValue
|
|
httpRequest.headerFields[.init("X-Apple-GS-Token")!] = loginToken.token
|
|
|
|
anisetteData.dictionary.forEach { httpRequest.headerFields[.init($0)!] = $1 }
|
|
|
|
request.configure(urlRequest: &httpRequest)
|
|
|
|
let body = try request.apiVersion.body(withParameters: request.parameters)
|
|
|
|
let (_, data) = try await httpClient.makeRequest(httpRequest, body: body)
|
|
|
|
let decoded: R.Response = try request.apiVersion.decode(response: data)
|
|
|
|
return try await request.parse(decoded)
|
|
}
|
|
|
|
public func send<R: DeveloperServicesRequest>(_ request: R) async throws -> R.Value {
|
|
let anisetteData = try await anisetteDataProvider.fetchAnisetteData()
|
|
return try await self.send(request, anisetteData: anisetteData)
|
|
}
|
|
|
|
}
|