[AltServer] Supports Install/Remove provisioning profiles requests

Stuff I shoulda committed
This commit is contained in:
Riley Testut
2020-03-05 14:49:21 -08:00
parent afdefc23ce
commit 27bce4e456
9 changed files with 637 additions and 84 deletions

View File

@@ -29,6 +29,11 @@ public extension Bundle
return infoPlistURL
}
var provisioningProfileURL: URL {
let infoPlistURL = self.bundleURL.appendingPathComponent("embedded.mobileprovision")
return infoPlistURL
}
var certificateURL: URL {
let infoPlistURL = self.bundleURL.appendingPathComponent("ALTCertificate.p12")
return infoPlistURL

View File

@@ -0,0 +1,58 @@
//
// CodableServerError.swift
// AltKit
//
// Created by Riley Testut on 3/5/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
// Can only automatically conform ALTServerError.Code to Codable, not ALTServerError itself
extension ALTServerError.Code: Codable {}
struct CodableServerError: Codable
{
var error: ALTServerError {
return ALTServerError(self.errorCode, userInfo: self.userInfo ?? [:])
}
private var errorCode: ALTServerError.Code
private var userInfo: [String: String]?
private enum CodingKeys: String, CodingKey
{
case errorCode
case userInfo
}
init(error: ALTServerError)
{
self.errorCode = error.code
let userInfo = error.userInfo.compactMapValues { $0 as? String }
if !userInfo.isEmpty
{
self.userInfo = userInfo
}
}
init(from decoder: Decoder) throws
{
let container = try decoder.container(keyedBy: CodingKeys.self)
let errorCode = try container.decode(Int.self, forKey: .errorCode)
self.errorCode = ALTServerError.Code(rawValue: errorCode) ?? .unknown
let userInfo = try container.decodeIfPresent([String: String].self, forKey: .userInfo)
self.userInfo = userInfo
}
func encode(to encoder: Encoder) throws
{
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.error.code.rawValue, forKey: .errorCode)
try container.encodeIfPresent(self.userInfo, forKey: .userInfo)
}
}

View File

@@ -11,6 +11,9 @@
extern NSErrorDomain const AltServerErrorDomain;
extern NSErrorDomain const AltServerInstallationErrorDomain;
extern NSErrorUserInfoKey const ALTUnderlyingErrorCodeErrorKey;
extern NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey;
typedef NS_ERROR_ENUM(AltServerErrorDomain, ALTServerError)
{
ALTServerErrorUnknown = 0,
@@ -32,7 +35,11 @@ typedef NS_ERROR_ENUM(AltServerErrorDomain, ALTServerError)
ALTServerErrorUnknownResponse = 12,
ALTServerErrorInvalidAnisetteData = 13,
ALTServerErrorPluginNotFound = 14
ALTServerErrorPluginNotFound = 14,
ALTServerErrorProfileInstallFailed = 15,
ALTServerErrorProfileCopyFailed = 16,
ALTServerErrorProfileRemoveFailed = 17,
};
NS_ASSUME_NONNULL_BEGIN

View File

@@ -11,6 +11,9 @@
NSErrorDomain const AltServerErrorDomain = @"com.rileytestut.AltServer";
NSErrorDomain const AltServerInstallationErrorDomain = @"com.rileytestut.AltServer.Installation";
NSErrorUserInfoKey const ALTUnderlyingErrorCodeErrorKey = @"underlyingErrorCode";
NSErrorUserInfoKey const ALTProvisioningProfileBundleIDErrorKey = @"bundleIdentifier";
@implementation NSError (ALTServerError)
+ (void)load
@@ -73,7 +76,41 @@ NSErrorDomain const AltServerInstallationErrorDomain = @"com.rileytestut.AltServ
case ALTServerErrorPluginNotFound:
return NSLocalizedString(@"Could not connect to Mail plug-in. Please make sure Mail is running and that you've enabled the plug-in in Mail's preferences, then try again.", @"");
case ALTServerErrorProfileInstallFailed:
return [self underlyingProfileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not install profile", "")];
case ALTServerErrorProfileCopyFailed:
return [self underlyingProfileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not copy provisioning profiles", "")];
case ALTServerErrorProfileRemoveFailed:
return [self underlyingProfileErrorLocalizedDescriptionWithBaseDescription:NSLocalizedString(@"Could not remove profile", "")];
}
}
- (NSString *)underlyingProfileErrorLocalizedDescriptionWithBaseDescription:(NSString *)baseDescription
{
NSMutableString *localizedDescription = [NSMutableString string];
NSString *bundleID = self.userInfo[ALTProvisioningProfileBundleIDErrorKey];
if (bundleID)
{
[localizedDescription appendFormat:NSLocalizedString(@"%@ “%@”", @""), baseDescription, bundleID];
}
else
{
[localizedDescription appendString:baseDescription];
}
NSString *underlyingErrorCode = self.userInfo[ALTUnderlyingErrorCodeErrorKey];
if (underlyingErrorCode)
{
[localizedDescription appendFormat:@" (%@)", underlyingErrorCode];
}
[localizedDescription appendString:@"."];
return localizedDescription;
}
@end

View File

@@ -11,9 +11,6 @@ import AltSign
public let ALTServerServiceType = "_altserver._tcp"
// Can only automatically conform ALTServerError.Code to Codable, not ALTServerError itself
extension ALTServerError.Code: Codable {}
protocol ServerMessageProtocol: Codable
{
var version: Int { get }
@@ -25,6 +22,8 @@ public enum ServerRequest: Decodable
case anisetteData(AnisetteDataRequest)
case prepareApp(PrepareAppRequest)
case beginInstallation(BeginInstallationRequest)
case installProvisioningProfiles(InstallProvisioningProfilesRequest)
case removeProvisioningProfiles(RemoveProvisioningProfilesRequest)
case unknown(identifier: String, version: Int)
var identifier: String {
@@ -33,6 +32,8 @@ public enum ServerRequest: Decodable
case .anisetteData(let request): return request.identifier
case .prepareApp(let request): return request.identifier
case .beginInstallation(let request): return request.identifier
case .installProvisioningProfiles(let request): return request.identifier
case .removeProvisioningProfiles(let request): return request.identifier
case .unknown(let identifier, _): return identifier
}
}
@@ -43,6 +44,8 @@ public enum ServerRequest: Decodable
case .anisetteData(let request): return request.version
case .prepareApp(let request): return request.version
case .beginInstallation(let request): return request.version
case .installProvisioningProfiles(let request): return request.version
case .removeProvisioningProfiles(let request): return request.version
case .unknown(_, let version): return version
}
}
@@ -73,6 +76,14 @@ public enum ServerRequest: Decodable
case "BeginInstallationRequest":
let request = try BeginInstallationRequest(from: decoder)
self = .beginInstallation(request)
case "InstallProvisioningProfilesRequest":
let request = try InstallProvisioningProfilesRequest(from: decoder)
self = .installProvisioningProfiles(request)
case "RemoveProvisioningProfilesRequest":
let request = try RemoveProvisioningProfilesRequest(from: decoder)
self = .removeProvisioningProfiles(request)
default:
self = .unknown(identifier: identifier, version: version)
@@ -84,6 +95,8 @@ public enum ServerResponse: Decodable
{
case anisetteData(AnisetteDataResponse)
case installationProgress(InstallationProgressResponse)
case installProvisioningProfiles(InstallProvisioningProfilesResponse)
case removeProvisioningProfiles(RemoveProvisioningProfilesResponse)
case error(ErrorResponse)
case unknown(identifier: String, version: Int)
@@ -92,6 +105,8 @@ public enum ServerResponse: Decodable
{
case .anisetteData(let response): return response.identifier
case .installationProgress(let response): return response.identifier
case .installProvisioningProfiles(let response): return response.identifier
case .removeProvisioningProfiles(let response): return response.identifier
case .error(let response): return response.identifier
case .unknown(let identifier, _): return identifier
}
@@ -102,6 +117,8 @@ public enum ServerResponse: Decodable
{
case .anisetteData(let response): return response.version
case .installationProgress(let response): return response.version
case .installProvisioningProfiles(let response): return response.version
case .removeProvisioningProfiles(let response): return response.version
case .error(let response): return response.version
case .unknown(_, let version): return version
}
@@ -129,6 +146,14 @@ public enum ServerResponse: Decodable
case "InstallationProgressResponse":
let response = try InstallationProgressResponse(from: decoder)
self = .installationProgress(response)
case "InstallProvisioningProfilesResponse":
let response = try InstallProvisioningProfilesResponse(from: decoder)
self = .installProvisioningProfiles(response)
case "RemoveProvisioningProfilesResponse":
let response = try RemoveProvisioningProfilesResponse(from: decoder)
self = .removeProvisioningProfiles(response)
case "ErrorResponse":
let response = try ErrorResponse(from: decoder)
@@ -140,6 +165,28 @@ public enum ServerResponse: Decodable
}
}
// _Don't_ provide generic SuccessResponse, as that would prevent us
// from easily changing response format for a request in the future.
public struct ErrorResponse: ServerMessageProtocol
{
public var version = 2
public var identifier = "ErrorResponse"
public var error: ALTServerError {
return self.serverError?.error ?? ALTServerError(self.errorCode)
}
private var serverError: CodableServerError?
// Legacy (v1)
private var errorCode: ALTServerError.Code
public init(error: ALTServerError)
{
self.serverError = CodableServerError(error: error)
self.errorCode = error.code
}
}
public struct AnisetteDataRequest: ServerMessageProtocol
{
public var version = 1
@@ -223,22 +270,6 @@ public struct BeginInstallationRequest: ServerMessageProtocol
}
}
public struct ErrorResponse: ServerMessageProtocol
{
public var version = 1
public var identifier = "ErrorResponse"
public var error: ALTServerError {
return ALTServerError(self.errorCode)
}
private var errorCode: ALTServerError.Code
public init(error: ALTServerError)
{
self.errorCode = error.code
}
}
public struct InstallationProgressResponse: ServerMessageProtocol
{
public var version = 1
@@ -251,3 +282,96 @@ public struct InstallationProgressResponse: ServerMessageProtocol
self.progress = progress
}
}
public struct InstallProvisioningProfilesRequest: ServerMessageProtocol
{
public var version = 1
public var identifier = "InstallProvisioningProfilesRequest"
public var udid: String
public var provisioningProfiles: Set<ALTProvisioningProfile>
// If activeProfiles is non-nil, then AltServer should remove all profiles except active ones.
public var activeProfiles: Set<String>?
private enum CodingKeys: String, CodingKey
{
case identifier
case version
case udid
case provisioningProfiles
case activeProfiles
}
public init(udid: String, provisioningProfiles: Set<ALTProvisioningProfile>, activeProfiles: Set<String>?)
{
self.udid = udid
self.provisioningProfiles = provisioningProfiles
self.activeProfiles = activeProfiles
}
public init(from decoder: Decoder) throws
{
let container = try decoder.container(keyedBy: CodingKeys.self)
self.version = try container.decode(Int.self, forKey: .version)
self.identifier = try container.decode(String.self, forKey: .identifier)
self.udid = try container.decode(String.self, forKey: .udid)
let rawProvisioningProfiles = try container.decode([Data].self, forKey: .provisioningProfiles)
let provisioningProfiles = try rawProvisioningProfiles.map { (data) -> ALTProvisioningProfile in
guard let profile = ALTProvisioningProfile(data: data) else {
throw DecodingError.dataCorruptedError(forKey: CodingKeys.provisioningProfiles, in: container, debugDescription: "Could not parse provisioning profile from data.")
}
return profile
}
self.provisioningProfiles = Set(provisioningProfiles)
self.activeProfiles = try container.decodeIfPresent(Set<String>.self, forKey: .activeProfiles)
}
public func encode(to encoder: Encoder) throws
{
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.version, forKey: .version)
try container.encode(self.identifier, forKey: .identifier)
try container.encode(self.udid, forKey: .udid)
try container.encode(self.provisioningProfiles.map { $0.data }, forKey: .provisioningProfiles)
try container.encodeIfPresent(self.activeProfiles, forKey: .activeProfiles)
}
}
public struct InstallProvisioningProfilesResponse: ServerMessageProtocol
{
public var version = 1
public var identifier = "InstallProvisioningProfilesResponse"
public init()
{
}
}
public struct RemoveProvisioningProfilesRequest: ServerMessageProtocol
{
public var version = 1
public var identifier = "RemoveProvisioningProfilesRequest"
public var udid: String
public var bundleIdentifiers: Set<String>
public init(udid: String, bundleIdentifiers: Set<String>)
{
self.udid = udid
self.bundleIdentifiers = bundleIdentifiers
}
}
public struct RemoveProvisioningProfilesResponse: ServerMessageProtocol
{
public var version = 1
public var identifier = "RemoveProvisioningProfilesResponse"
public init()
{
}
}