mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
[AltServer] Moves core ConnectionManager logic to AltKit
Refactors ConnectionManager to use arbitrary RequestHandlers and ConnectionHandlers. This allows the core AltServer request logic to be shared across different targets with different connection types.
This commit is contained in:
@@ -8,5 +8,6 @@
|
||||
|
||||
#import "NSError+ALTServerError.h"
|
||||
#import "CFNotificationName+AltStore.h"
|
||||
#import "ALTConnection.h"
|
||||
|
||||
extern uint16_t ALTDeviceListeningSocket;
|
||||
|
||||
23
AltKit/Connections/ALTConnection.h
Normal file
23
AltKit/Connections/ALTConnection.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// ALTConnection.h
|
||||
// AltKit
|
||||
//
|
||||
// Created by Riley Testut on 6/1/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NS_SWIFT_NAME(Connection)
|
||||
@protocol ALTConnection <NSObject>
|
||||
|
||||
- (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler NS_REFINED_FOR_SWIFT;
|
||||
- (void)receiveDataWithExpectedSize:(NSInteger)expectedSize completionHandler:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler NS_SWIFT_NAME(__receiveData(expectedSize:completionHandler:));
|
||||
|
||||
- (void)disconnect;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
113
AltKit/Connections/Connection.swift
Normal file
113
AltKit/Connections/Connection.swift
Normal file
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Connection.swift
|
||||
// AltKit
|
||||
//
|
||||
// Created by Riley Testut on 6/1/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
public extension Connection
|
||||
{
|
||||
func send(_ data: Data, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
||||
{
|
||||
self.__send(data) { (success, error) in
|
||||
let result = Result(success, error).mapError { (error) -> ALTServerError in
|
||||
guard let nwError = error as? NWError else { return ALTServerError(error) }
|
||||
return ALTServerError(.lostConnection, underlyingError: nwError)
|
||||
}
|
||||
|
||||
completionHandler(result)
|
||||
}
|
||||
}
|
||||
|
||||
func receiveData(expectedSize: Int, completionHandler: @escaping (Result<Data, ALTServerError>) -> Void)
|
||||
{
|
||||
self.__receiveData(expectedSize: expectedSize) { (data, error) in
|
||||
let result = Result(data, error).mapError { (error) -> ALTServerError in
|
||||
guard let nwError = error as? NWError else { return ALTServerError(error) }
|
||||
return ALTServerError(.lostConnection, underlyingError: nwError)
|
||||
}
|
||||
|
||||
completionHandler(result)
|
||||
}
|
||||
}
|
||||
|
||||
func send<T: Encodable>(_ response: T, shouldDisconnect: Bool = false, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
||||
{
|
||||
func finish(_ result: Result<Void, ALTServerError>)
|
||||
{
|
||||
completionHandler(result)
|
||||
|
||||
if shouldDisconnect
|
||||
{
|
||||
// Add short delay to prevent us from dropping connection too quickly.
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
|
||||
self.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
let data = try JSONEncoder().encode(response)
|
||||
let responseSize = withUnsafeBytes(of: Int32(data.count)) { Data($0) }
|
||||
|
||||
self.send(responseSize) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success:
|
||||
self.send(data) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success: finish(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(.failure(.init(.invalidResponse, underlyingError: error)))
|
||||
}
|
||||
}
|
||||
|
||||
func receiveRequest(completionHandler: @escaping (Result<ServerRequest, ALTServerError>) -> Void)
|
||||
{
|
||||
let size = MemoryLayout<Int32>.size
|
||||
|
||||
print("Receiving request size from connection:", self)
|
||||
self.receiveData(expectedSize: size) { (result) in
|
||||
do
|
||||
{
|
||||
let data = try result.get()
|
||||
|
||||
let expectedSize = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
||||
print("Receiving request from connection: \(self)... (\(expectedSize) bytes)")
|
||||
|
||||
self.receiveData(expectedSize: expectedSize) { (result) in
|
||||
do
|
||||
{
|
||||
let data = try result.get()
|
||||
let request = try JSONDecoder().decode(ServerRequest.self, from: data)
|
||||
|
||||
print("Received request:", request)
|
||||
completionHandler(.success(request))
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
160
AltKit/Connections/ConnectionManager.swift
Normal file
160
AltKit/Connections/ConnectionManager.swift
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// ConnectionManager.swift
|
||||
// AltServer
|
||||
//
|
||||
// Created by Riley Testut on 5/23/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
public protocol RequestHandler
|
||||
{
|
||||
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: Connection, completionHandler: @escaping (Result<AnisetteDataResponse, Error>) -> Void)
|
||||
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: Connection, completionHandler: @escaping (Result<InstallationProgressResponse, Error>) -> Void)
|
||||
|
||||
func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: Connection,
|
||||
completionHandler: @escaping (Result<InstallProvisioningProfilesResponse, Error>) -> Void)
|
||||
func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: Connection,
|
||||
completionHandler: @escaping (Result<RemoveProvisioningProfilesResponse, Error>) -> Void)
|
||||
|
||||
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: Connection, completionHandler: @escaping (Result<RemoveAppResponse, Error>) -> Void)
|
||||
}
|
||||
|
||||
public protocol ConnectionHandler: AnyObject
|
||||
{
|
||||
var connectionHandler: ((Connection) -> Void)? { get set }
|
||||
var disconnectionHandler: ((Connection) -> Void)? { get set }
|
||||
|
||||
func startListening()
|
||||
func stopListening()
|
||||
}
|
||||
|
||||
public class ConnectionManager<RequestHandlerType: RequestHandler>
|
||||
{
|
||||
public let requestHandler: RequestHandlerType
|
||||
public let connectionHandlers: [ConnectionHandler]
|
||||
|
||||
public var isStarted = false
|
||||
|
||||
private var connections = [Connection]()
|
||||
|
||||
public init(requestHandler: RequestHandlerType, connectionHandlers: [ConnectionHandler])
|
||||
{
|
||||
self.requestHandler = requestHandler
|
||||
self.connectionHandlers = connectionHandlers
|
||||
|
||||
for handler in connectionHandlers
|
||||
{
|
||||
handler.connectionHandler = { [weak self] (connection) in
|
||||
self?.prepare(connection)
|
||||
}
|
||||
|
||||
handler.disconnectionHandler = { [weak self] (connection) in
|
||||
self?.disconnect(connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func start()
|
||||
{
|
||||
guard !self.isStarted else { return }
|
||||
|
||||
for connectionHandler in self.connectionHandlers
|
||||
{
|
||||
connectionHandler.startListening()
|
||||
}
|
||||
|
||||
self.isStarted = true
|
||||
}
|
||||
|
||||
public func stop()
|
||||
{
|
||||
guard self.isStarted else { return }
|
||||
|
||||
for connectionHandler in self.connectionHandlers
|
||||
{
|
||||
connectionHandler.stopListening()
|
||||
}
|
||||
|
||||
self.isStarted = false
|
||||
}
|
||||
}
|
||||
|
||||
private extension ConnectionManager
|
||||
{
|
||||
func prepare(_ connection: Connection)
|
||||
{
|
||||
guard !self.connections.contains(where: { $0 === connection }) else { return }
|
||||
self.connections.append(connection)
|
||||
|
||||
self.handleRequest(for: connection)
|
||||
}
|
||||
|
||||
func disconnect(_ connection: Connection)
|
||||
{
|
||||
guard let index = self.connections.firstIndex(where: { $0 === connection }) else { return }
|
||||
self.connections.remove(at: index)
|
||||
}
|
||||
|
||||
func handleRequest(for connection: Connection)
|
||||
{
|
||||
func finish<T: ServerMessageProtocol>(_ result: Result<T, Error>)
|
||||
{
|
||||
do
|
||||
{
|
||||
let response = try result.get()
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent response \(response) with result:", result)
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
let response = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent error response \(response) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connection.receiveRequest() { (result) in
|
||||
print("Received request with result:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(Result<ErrorResponse, Error>.failure(error))
|
||||
|
||||
case .success(.anisetteData(let request)):
|
||||
self.requestHandler.handleAnisetteDataRequest(request, for: connection) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case .success(.prepareApp(let request)):
|
||||
self.requestHandler.handlePrepareAppRequest(request, for: connection) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case .success(.beginInstallation): break
|
||||
|
||||
case .success(.installProvisioningProfiles(let request)):
|
||||
self.requestHandler.handleInstallProvisioningProfilesRequest(request, for: connection) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case .success(.removeProvisioningProfiles(let request)):
|
||||
self.requestHandler.handleRemoveProvisioningProfilesRequest(request, for: connection) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case .success(.removeApp(let request)):
|
||||
self.requestHandler.handleRemoveAppRequest(request, for: connection) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case .success(.unknown):
|
||||
finish(Result<ErrorResponse, Error>.failure(ALTServerError(.unknownRequest)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
AltKit/Connections/NetworkConnection.swift
Normal file
50
AltKit/Connections/NetworkConnection.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// NetworkConnection.swift
|
||||
// AltKit
|
||||
//
|
||||
// Created by Riley Testut on 6/1/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
public class NetworkConnection: NSObject, Connection
|
||||
{
|
||||
public let nwConnection: NWConnection
|
||||
|
||||
public init(_ nwConnection: NWConnection)
|
||||
{
|
||||
self.nwConnection = nwConnection
|
||||
}
|
||||
|
||||
public func __send(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void)
|
||||
{
|
||||
self.nwConnection.send(content: data, completion: .contentProcessed { (error) in
|
||||
completionHandler(error == nil, error)
|
||||
})
|
||||
}
|
||||
|
||||
public func __receiveData(expectedSize: Int, completionHandler: @escaping (Data?, Error?) -> Void)
|
||||
{
|
||||
self.nwConnection.receive(minimumIncompleteLength: expectedSize, maximumLength: expectedSize) { (data, context, isComplete, error) in
|
||||
completionHandler(data, error)
|
||||
}
|
||||
}
|
||||
|
||||
public func disconnect()
|
||||
{
|
||||
switch self.nwConnection.state
|
||||
{
|
||||
case .cancelled, .failed: break
|
||||
default: self.nwConnection.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NetworkConnection
|
||||
{
|
||||
override public var description: String {
|
||||
return "\(self.nwConnection.endpoint) (Network)"
|
||||
}
|
||||
}
|
||||
36
AltKit/Extensions/ALTServerError+Conveniences.swift
Normal file
36
AltKit/Extensions/ALTServerError+Conveniences.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// ALTServerError+Conveniences.swift
|
||||
// AltKit
|
||||
//
|
||||
// Created by Riley Testut on 6/4/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension ALTServerError
|
||||
{
|
||||
init<E: Error>(_ error: E)
|
||||
{
|
||||
switch error
|
||||
{
|
||||
case let error as ALTServerError: self = error
|
||||
case is DecodingError: self = ALTServerError(.invalidRequest, underlyingError: error)
|
||||
case is EncodingError: self = ALTServerError(.invalidResponse, underlyingError: error)
|
||||
case let error as NSError:
|
||||
var userInfo = error.userInfo
|
||||
if !userInfo.keys.contains(NSUnderlyingErrorKey)
|
||||
{
|
||||
// Assign underlying error (if there isn't already one).
|
||||
userInfo[NSUnderlyingErrorKey] = error
|
||||
}
|
||||
|
||||
self = ALTServerError(.unknown, userInfo: error.userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
init<E: Error>(_ code: ALTServerError.Code, underlyingError: E)
|
||||
{
|
||||
self = ALTServerError(.invalidRequest, userInfo: [NSUnderlyingErrorKey: underlyingError])
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ALTWiredConnection ()
|
||||
|
||||
@property (nonatomic, readwrite, getter=isConnected) BOOL connected;
|
||||
|
||||
@property (nonatomic, readonly) idevice_connection_t connection;
|
||||
|
||||
- (instancetype)initWithDevice:(ALTDevice *)device connection:(idevice_connection_t)connection;
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
|
||||
#import <AltSign/AltSign.h>
|
||||
|
||||
#import "AltKit.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NS_SWIFT_NAME(WiredConnection)
|
||||
@interface ALTWiredConnection : NSObject
|
||||
@interface ALTWiredConnection : NSObject <ALTConnection>
|
||||
|
||||
@property (nonatomic, readonly, getter=isConnected) BOOL connected;
|
||||
|
||||
@property (nonatomic, copy, readonly) ALTDevice *device;
|
||||
|
||||
|
||||
@@ -30,8 +30,15 @@
|
||||
|
||||
- (void)disconnect
|
||||
{
|
||||
if (![self isConnected])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
idevice_disconnect(self.connection);
|
||||
_connection = nil;
|
||||
|
||||
self.connected = NO;
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
|
||||
@@ -98,4 +105,11 @@
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - NSObject -
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@ (Wired)", self.device.name];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
//
|
||||
// ClientConnection.swift
|
||||
// AltServer
|
||||
//
|
||||
// Created by Riley Testut on 1/9/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
import AltKit
|
||||
import AltSign
|
||||
|
||||
extension ClientConnection
|
||||
{
|
||||
enum Connection
|
||||
{
|
||||
case wireless(NWConnection)
|
||||
case wired(WiredConnection)
|
||||
}
|
||||
}
|
||||
|
||||
class ClientConnection
|
||||
{
|
||||
let connection: Connection
|
||||
|
||||
init(connection: Connection)
|
||||
{
|
||||
self.connection = connection
|
||||
}
|
||||
|
||||
func disconnect()
|
||||
{
|
||||
switch self.connection
|
||||
{
|
||||
case .wireless(let connection):
|
||||
switch connection.state
|
||||
{
|
||||
case .cancelled, .failed:
|
||||
print("Disconnecting from \(connection.endpoint)...")
|
||||
|
||||
default:
|
||||
// State update handler might call this method again.
|
||||
connection.cancel()
|
||||
}
|
||||
|
||||
case .wired(let connection):
|
||||
connection.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
func send<T: Encodable>(_ response: T, shouldDisconnect: Bool = false, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
||||
{
|
||||
func finish(_ result: Result<Void, ALTServerError>)
|
||||
{
|
||||
completionHandler(result)
|
||||
|
||||
if shouldDisconnect
|
||||
{
|
||||
// Add short delay to prevent us from dropping connection too quickly.
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
|
||||
self.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
let data = try JSONEncoder().encode(response)
|
||||
let responseSize = withUnsafeBytes(of: Int32(data.count)) { Data($0) }
|
||||
|
||||
self.send(responseSize) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure: finish(.failure(.init(.lostConnection)))
|
||||
case .success:
|
||||
|
||||
self.send(data) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure: finish(.failure(.init(.lostConnection)))
|
||||
case .success: finish(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(.failure(.init(.invalidResponse)))
|
||||
}
|
||||
}
|
||||
|
||||
func receiveRequest(completionHandler: @escaping (Result<ServerRequest, ALTServerError>) -> Void)
|
||||
{
|
||||
let size = MemoryLayout<Int32>.size
|
||||
|
||||
print("Receiving request size")
|
||||
self.receiveData(expectedBytes: size) { (result) in
|
||||
do
|
||||
{
|
||||
let data = try result.get()
|
||||
|
||||
print("Receiving request...")
|
||||
let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
||||
self.receiveData(expectedBytes: expectedBytes) { (result) in
|
||||
do
|
||||
{
|
||||
let data = try result.get()
|
||||
let request = try JSONDecoder().decode(ServerRequest.self, from: data)
|
||||
|
||||
print("Received installation request:", request)
|
||||
completionHandler(.success(request))
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func send(_ data: Data, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||
{
|
||||
switch self.connection
|
||||
{
|
||||
case .wireless(let connection):
|
||||
connection.send(content: data, completion: .contentProcessed { (error) in
|
||||
if let error = error
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
})
|
||||
|
||||
case .wired(let connection):
|
||||
connection.send(data) { (success, error) in
|
||||
if !success
|
||||
{
|
||||
completionHandler(.failure(ALTServerError(.lostConnection)))
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receiveData(expectedBytes: Int, completionHandler: @escaping (Result<Data, Error>) -> Void)
|
||||
{
|
||||
func finish(data: Data?, error: Error?)
|
||||
{
|
||||
do
|
||||
{
|
||||
let data = try self.process(data: data, error: error)
|
||||
completionHandler(.success(data))
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
|
||||
switch self.connection
|
||||
{
|
||||
case .wireless(let connection):
|
||||
connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in
|
||||
finish(data: data, error: error)
|
||||
}
|
||||
|
||||
case .wired(let connection):
|
||||
connection.receiveData(withExpectedSize: expectedBytes) { (data, error) in
|
||||
finish(data: data, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ClientConnection: CustomStringConvertible
|
||||
{
|
||||
var description: String {
|
||||
switch self.connection
|
||||
{
|
||||
case .wireless(let connection): return "\(connection.endpoint) (Wireless)"
|
||||
case .wired(let connection): return "\(connection.device.name) (Wired)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ClientConnection
|
||||
{
|
||||
func process(data: Data?, error: Error?) throws -> Data
|
||||
{
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
guard let data = data else { throw error ?? ALTServerError(.unknown) }
|
||||
return data
|
||||
}
|
||||
catch let error as NWError
|
||||
{
|
||||
print("Error receiving data from connection \(connection)", error)
|
||||
|
||||
throw ALTServerError(.lostConnection)
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw error
|
||||
}
|
||||
}
|
||||
catch let error as ALTServerError
|
||||
{
|
||||
throw error
|
||||
}
|
||||
catch
|
||||
{
|
||||
preconditionFailure("A non-ALTServerError should never be thrown from this method.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
//
|
||||
// ConnectionManager.swift
|
||||
// AltServer
|
||||
//
|
||||
// Created by Riley Testut on 5/23/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import AppKit
|
||||
|
||||
import AltKit
|
||||
|
||||
extension ALTServerError
|
||||
{
|
||||
init<E: Error>(_ error: E)
|
||||
{
|
||||
switch error
|
||||
{
|
||||
case let error as ALTServerError: self = error
|
||||
case is DecodingError: self = ALTServerError(.invalidRequest)
|
||||
case is EncodingError: self = ALTServerError(.invalidResponse)
|
||||
case let error as NSError:
|
||||
self = ALTServerError(.unknown, userInfo: error.userInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ConnectionManager
|
||||
{
|
||||
enum State
|
||||
{
|
||||
case notRunning
|
||||
case connecting
|
||||
case running(NWListener.Service)
|
||||
case failed(Swift.Error)
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionManager
|
||||
{
|
||||
static let shared = ConnectionManager()
|
||||
|
||||
var stateUpdateHandler: ((State) -> Void)?
|
||||
|
||||
private(set) var state: State = .notRunning {
|
||||
didSet {
|
||||
self.stateUpdateHandler?(self.state)
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var listener = self.makeListener()
|
||||
private let dispatchQueue = DispatchQueue(label: "com.rileytestut.AltServer.connections", qos: .utility)
|
||||
|
||||
private var connections = [ClientConnection]()
|
||||
private var notificationConnections = [ALTDevice: NotificationConnection]()
|
||||
|
||||
private init()
|
||||
{
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(ConnectionManager.deviceDidConnect(_:)), name: .deviceManagerDeviceDidConnect, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(ConnectionManager.deviceDidDisconnect(_:)), name: .deviceManagerDeviceDidDisconnect, object: nil)
|
||||
}
|
||||
|
||||
func start()
|
||||
{
|
||||
switch self.state
|
||||
{
|
||||
case .notRunning, .failed: self.listener.start(queue: self.dispatchQueue)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
func stop()
|
||||
{
|
||||
switch self.state
|
||||
{
|
||||
case .running: self.listener.cancel()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
func disconnect(_ connection: ClientConnection)
|
||||
{
|
||||
connection.disconnect()
|
||||
|
||||
if let index = self.connections.firstIndex(where: { $0 === connection })
|
||||
{
|
||||
self.connections.remove(at: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ConnectionManager
|
||||
{
|
||||
func makeListener() -> NWListener
|
||||
{
|
||||
let listener = try! NWListener(using: .tcp)
|
||||
|
||||
let service: NWListener.Service
|
||||
|
||||
if let serverID = UserDefaults.standard.serverID?.data(using: .utf8)
|
||||
{
|
||||
let txtDictionary = ["serverID": serverID]
|
||||
let txtData = NetService.data(fromTXTRecord: txtDictionary)
|
||||
|
||||
service = NWListener.Service(name: nil, type: ALTServerServiceType, domain: nil, txtRecord: txtData)
|
||||
}
|
||||
else
|
||||
{
|
||||
service = NWListener.Service(type: ALTServerServiceType)
|
||||
}
|
||||
|
||||
listener.service = service
|
||||
|
||||
listener.serviceRegistrationUpdateHandler = { (serviceChange) in
|
||||
switch serviceChange
|
||||
{
|
||||
case .add(.service(let name, let type, let domain, _)):
|
||||
let service = NWListener.Service(name: name, type: type, domain: domain, txtRecord: nil)
|
||||
self.state = .running(service)
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
listener.stateUpdateHandler = { (state) in
|
||||
switch state
|
||||
{
|
||||
case .ready: break
|
||||
case .waiting, .setup: self.state = .connecting
|
||||
case .cancelled: self.state = .notRunning
|
||||
case .failed(let error):
|
||||
self.state = .failed(error)
|
||||
self.start()
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
listener.newConnectionHandler = { [weak self] (connection) in
|
||||
self?.prepare(connection)
|
||||
}
|
||||
|
||||
return listener
|
||||
}
|
||||
|
||||
func prepare(_ connection: NWConnection)
|
||||
{
|
||||
let clientConnection = ClientConnection(connection: .wireless(connection))
|
||||
|
||||
guard !self.connections.contains(where: { $0 === clientConnection }) else { return }
|
||||
self.connections.append(clientConnection)
|
||||
|
||||
connection.stateUpdateHandler = { [weak self] (state) in
|
||||
switch state
|
||||
{
|
||||
case .setup, .preparing: break
|
||||
|
||||
case .ready:
|
||||
print("Connected to client:", connection.endpoint)
|
||||
self?.handleRequest(for: clientConnection)
|
||||
|
||||
case .waiting:
|
||||
print("Waiting for connection...")
|
||||
|
||||
case .failed(let error):
|
||||
print("Failed to connect to service \(connection.endpoint).", error)
|
||||
self?.disconnect(clientConnection)
|
||||
|
||||
case .cancelled:
|
||||
self?.disconnect(clientConnection)
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
connection.start(queue: self.dispatchQueue)
|
||||
}
|
||||
}
|
||||
|
||||
private extension ConnectionManager
|
||||
{
|
||||
func startNotificationConnection(to device: ALTDevice)
|
||||
{
|
||||
ALTDeviceManager.shared.startNotificationConnection(to: device) { (connection, error) in
|
||||
guard let connection = connection else { return }
|
||||
|
||||
let notifications: [CFNotificationName] = [.wiredServerConnectionAvailableRequest, .wiredServerConnectionStartRequest]
|
||||
connection.startListening(forNotifications: notifications.map { String($0.rawValue) }) { (success, error) in
|
||||
guard success else { return }
|
||||
|
||||
connection.receivedNotificationHandler = { [weak self, weak connection] (notification) in
|
||||
guard let self = self, let connection = connection else { return }
|
||||
self.handle(notification, for: connection)
|
||||
}
|
||||
|
||||
self.notificationConnections[device] = connection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopNotificationConnection(to device: ALTDevice)
|
||||
{
|
||||
guard let connection = self.notificationConnections[device] else { return }
|
||||
connection.disconnect()
|
||||
|
||||
self.notificationConnections[device] = nil
|
||||
}
|
||||
|
||||
func handle(_ notification: CFNotificationName, for connection: NotificationConnection)
|
||||
{
|
||||
switch notification
|
||||
{
|
||||
case .wiredServerConnectionAvailableRequest:
|
||||
connection.sendNotification(.wiredServerConnectionAvailableResponse) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Error sending wired server connection response.", error)
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Sent wired server connection available response!")
|
||||
}
|
||||
}
|
||||
|
||||
case .wiredServerConnectionStartRequest:
|
||||
ALTDeviceManager.shared.startWiredConnection(to: connection.device) { (wiredConnection, error) in
|
||||
if let wiredConnection = wiredConnection
|
||||
{
|
||||
print("Started wired server connection!")
|
||||
|
||||
let clientConnection = ClientConnection(connection: .wired(wiredConnection))
|
||||
self.handleRequest(for: clientConnection)
|
||||
}
|
||||
else if let error = error
|
||||
{
|
||||
print("Error starting wired server connection.", error)
|
||||
}
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ConnectionManager
|
||||
{
|
||||
func handleRequest(for connection: ClientConnection)
|
||||
{
|
||||
connection.receiveRequest() { (result) in
|
||||
print("Received initial request with result:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
let response = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent error response with result:", result)
|
||||
}
|
||||
|
||||
case .success(.anisetteData(let request)):
|
||||
self.handleAnisetteDataRequest(request, for: connection)
|
||||
|
||||
case .success(.prepareApp(let request)):
|
||||
self.handlePrepareAppRequest(request, for: connection)
|
||||
|
||||
case .success(.beginInstallation): break
|
||||
|
||||
case .success(.installProvisioningProfiles(let request)):
|
||||
self.handleInstallProvisioningProfilesRequest(request, for: connection)
|
||||
|
||||
case .success(.removeProvisioningProfiles(let request)):
|
||||
self.handleRemoveProvisioningProfilesRequest(request, for: connection)
|
||||
|
||||
case .success(.removeApp(let request)):
|
||||
self.handleRemoveAppRequest(request, for: connection)
|
||||
|
||||
case .success(.unknown):
|
||||
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent unknown request response with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: ClientConnection)
|
||||
{
|
||||
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(errorResponse, shouldDisconnect: true) { (result) in
|
||||
print("Sent anisette data error response with result:", result)
|
||||
}
|
||||
|
||||
case .success(let anisetteData):
|
||||
let response = AnisetteDataResponse(anisetteData: anisetteData)
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent anisette data response with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: ClientConnection)
|
||||
{
|
||||
var temporaryURL: URL?
|
||||
|
||||
func finish(_ result: Result<Void, ALTServerError>)
|
||||
{
|
||||
if let temporaryURL = temporaryURL
|
||||
{
|
||||
do { try FileManager.default.removeItem(at: temporaryURL) }
|
||||
catch { print("Failed to remove .ipa.", error) }
|
||||
}
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
print("Failed to process request from \(connection).", error)
|
||||
|
||||
let response = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent install app error response to \(connection) with result:", result)
|
||||
}
|
||||
|
||||
case .success:
|
||||
print("Processed request from \(connection).")
|
||||
|
||||
let response = InstallationProgressResponse(progress: 1.0)
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent install app response to \(connection) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.receiveApp(for: request, from: connection) { (result) in
|
||||
print("Received app with result:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success(let fileURL):
|
||||
temporaryURL = fileURL
|
||||
|
||||
print("Awaiting begin installation request...")
|
||||
|
||||
connection.receiveRequest() { (result) in
|
||||
print("Received begin installation request with result:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success(.beginInstallation(let installRequest)):
|
||||
print("Installing to device \(request.udid)...")
|
||||
|
||||
self.installApp(at: fileURL, toDeviceWithUDID: request.udid, activeProvisioningProfiles: installRequest.activeProfiles, connection: connection) { (result) in
|
||||
print("Installed to device with result:", result)
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success: finish(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
case .success:
|
||||
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent unknown request error response to \(connection) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receiveApp(for request: PrepareAppRequest, from connection: ClientConnection, completionHandler: @escaping (Result<URL, ALTServerError>) -> Void)
|
||||
{
|
||||
connection.receiveData(expectedBytes: request.contentSize) { (result) in
|
||||
do
|
||||
{
|
||||
print("Received app data!")
|
||||
|
||||
let data = try result.get()
|
||||
|
||||
guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) }
|
||||
|
||||
print("Writing app data...")
|
||||
|
||||
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".ipa")
|
||||
try data.write(to: temporaryURL, options: .atomic)
|
||||
|
||||
print("Wrote app to URL:", temporaryURL)
|
||||
|
||||
completionHandler(.success(temporaryURL))
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Error processing app data:", error)
|
||||
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func installApp(at fileURL: URL, toDeviceWithUDID udid: String, activeProvisioningProfiles: Set<String>?, connection: ClientConnection, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
||||
{
|
||||
let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default)
|
||||
var isSending = false
|
||||
|
||||
var observation: NSKeyValueObservation?
|
||||
|
||||
let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid, activeProvisioningProfiles: activeProvisioningProfiles) { (success, error) in
|
||||
print("Installed app with result:", error == nil ? "Success" : error!.localizedDescription)
|
||||
|
||||
if let error = error.map({ $0 as? ALTServerError ?? ALTServerError(.unknown) })
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
|
||||
observation?.invalidate()
|
||||
observation = nil
|
||||
}
|
||||
|
||||
observation = progress.observe(\.fractionCompleted, changeHandler: { (progress, change) in
|
||||
serialQueue.async {
|
||||
guard !isSending else { return }
|
||||
isSending = true
|
||||
|
||||
print("Progress:", progress.fractionCompleted)
|
||||
let response = InstallationProgressResponse(progress: progress.fractionCompleted)
|
||||
|
||||
connection.send(response) { (result) in
|
||||
serialQueue.async {
|
||||
isSending = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: ClientConnection)
|
||||
{
|
||||
ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error)
|
||||
|
||||
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(errorResponse, shouldDisconnect: true) { (result) in
|
||||
print("Sent install profiles error response with result:", result)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Installed profiles:", request.provisioningProfiles.map { $0.bundleIdentifier })
|
||||
|
||||
let response = InstallProvisioningProfilesResponse()
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent install profiles response to \(connection) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: ClientConnection)
|
||||
{
|
||||
ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Failed to remove profiles \(request.bundleIdentifiers):", error)
|
||||
|
||||
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(errorResponse, shouldDisconnect: true) { (result) in
|
||||
print("Sent remove profiles error response with result:", result)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Removed profiles:", request.bundleIdentifiers)
|
||||
|
||||
let response = RemoveProvisioningProfilesResponse()
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent remove profiles success response to \(connection) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: ClientConnection)
|
||||
{
|
||||
ALTDeviceManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier, fromDeviceWithUDID: request.udid) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Failed to remove app \(request.bundleIdentifier):", error)
|
||||
|
||||
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
||||
connection.send(errorResponse, shouldDisconnect: true) { (result) in
|
||||
print("Sent remove a[[ error response with result:", result)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Removed app:", request.bundleIdentifier)
|
||||
|
||||
let response = RemoveAppResponse()
|
||||
connection.send(response, shouldDisconnect: true) { (result) in
|
||||
print("Sent remove app success response to \(connection) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ConnectionManager
|
||||
{
|
||||
@objc func deviceDidConnect(_ notification: Notification)
|
||||
{
|
||||
guard let device = notification.object as? ALTDevice else { return }
|
||||
self.startNotificationConnection(to: device)
|
||||
}
|
||||
|
||||
@objc func deviceDidDisconnect(_ notification: Notification)
|
||||
{
|
||||
guard let device = notification.object as? ALTDevice else { return }
|
||||
self.stopNotificationConnection(to: device)
|
||||
}
|
||||
}
|
||||
219
AltServer/Connections/RequestHandler.swift
Normal file
219
AltServer/Connections/RequestHandler.swift
Normal file
@@ -0,0 +1,219 @@
|
||||
//
|
||||
// RequestHandler.swift
|
||||
// AltServer
|
||||
//
|
||||
// Created by Riley Testut on 5/23/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AltKit
|
||||
|
||||
typealias ConnectionManager = AltKit.ConnectionManager<RequestHandler>
|
||||
|
||||
private let connectionManager = ConnectionManager(requestHandler: RequestHandler(),
|
||||
connectionHandlers: [WirelessConnectionHandler(), WiredConnectionHandler()])
|
||||
|
||||
extension ConnectionManager
|
||||
{
|
||||
static var shared: ConnectionManager {
|
||||
return connectionManager
|
||||
}
|
||||
}
|
||||
|
||||
struct RequestHandler: AltKit.RequestHandler
|
||||
{
|
||||
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: Connection, completionHandler: @escaping (Result<AnisetteDataResponse, Error>) -> Void)
|
||||
{
|
||||
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): completionHandler(.failure(error))
|
||||
case .success(let anisetteData):
|
||||
let response = AnisetteDataResponse(anisetteData: anisetteData)
|
||||
completionHandler(.success(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: Connection, completionHandler: @escaping (Result<InstallationProgressResponse, Error>) -> Void)
|
||||
{
|
||||
var temporaryURL: URL?
|
||||
|
||||
func finish(_ result: Result<InstallationProgressResponse, Error>)
|
||||
{
|
||||
if let temporaryURL = temporaryURL
|
||||
{
|
||||
do { try FileManager.default.removeItem(at: temporaryURL) }
|
||||
catch { print("Failed to remove .ipa.", error) }
|
||||
}
|
||||
|
||||
completionHandler(result)
|
||||
}
|
||||
|
||||
self.receiveApp(for: request, from: connection) { (result) in
|
||||
print("Received app with result:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success(let fileURL):
|
||||
temporaryURL = fileURL
|
||||
|
||||
print("Awaiting begin installation request...")
|
||||
|
||||
connection.receiveRequest() { (result) in
|
||||
print("Received begin installation request with result:", result)
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success(.beginInstallation(let installRequest)):
|
||||
print("Installing app to device \(request.udid)...")
|
||||
|
||||
self.installApp(at: fileURL, toDeviceWithUDID: request.udid, activeProvisioningProfiles: installRequest.activeProfiles, connection: connection) { (result) in
|
||||
print("Installed app to device with result:", result)
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): finish(.failure(error))
|
||||
case .success:
|
||||
let response = InstallationProgressResponse(progress: 1.0)
|
||||
finish(.success(response))
|
||||
}
|
||||
}
|
||||
|
||||
case .success: finish(.failure(ALTServerError(.unknownRequest)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleInstallProvisioningProfilesRequest(_ request: InstallProvisioningProfilesRequest, for connection: Connection,
|
||||
completionHandler: @escaping (Result<InstallProvisioningProfilesResponse, Error>) -> Void)
|
||||
{
|
||||
ALTDeviceManager.shared.installProvisioningProfiles(request.provisioningProfiles, toDeviceWithUDID: request.udid, activeProvisioningProfiles: request.activeProfiles) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Failed to install profiles \(request.provisioningProfiles.map { $0.bundleIdentifier }):", error)
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Installed profiles:", request.provisioningProfiles.map { $0.bundleIdentifier })
|
||||
|
||||
let response = InstallProvisioningProfilesResponse()
|
||||
completionHandler(.success(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleRemoveProvisioningProfilesRequest(_ request: RemoveProvisioningProfilesRequest, for connection: Connection,
|
||||
completionHandler: @escaping (Result<RemoveProvisioningProfilesResponse, Error>) -> Void)
|
||||
{
|
||||
ALTDeviceManager.shared.removeProvisioningProfiles(forBundleIdentifiers: request.bundleIdentifiers, fromDeviceWithUDID: request.udid) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Failed to remove profiles \(request.bundleIdentifiers):", error)
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Removed profiles:", request.bundleIdentifiers)
|
||||
|
||||
let response = RemoveProvisioningProfilesResponse()
|
||||
completionHandler(.success(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleRemoveAppRequest(_ request: RemoveAppRequest, for connection: Connection, completionHandler: @escaping (Result<RemoveAppResponse, Error>) -> Void)
|
||||
{
|
||||
ALTDeviceManager.shared.removeApp(forBundleIdentifier: request.bundleIdentifier, fromDeviceWithUDID: request.udid) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Failed to remove app \(request.bundleIdentifier):", error)
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Removed app:", request.bundleIdentifier)
|
||||
|
||||
let response = RemoveAppResponse()
|
||||
completionHandler(.success(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension RequestHandler
|
||||
{
|
||||
func receiveApp(for request: PrepareAppRequest, from connection: Connection, completionHandler: @escaping (Result<URL, ALTServerError>) -> Void)
|
||||
{
|
||||
connection.receiveData(expectedSize: request.contentSize) { (result) in
|
||||
do
|
||||
{
|
||||
print("Received app data!")
|
||||
|
||||
let data = try result.get()
|
||||
|
||||
guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) }
|
||||
|
||||
print("Writing app data...")
|
||||
|
||||
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".ipa")
|
||||
try data.write(to: temporaryURL, options: .atomic)
|
||||
|
||||
print("Wrote app to URL:", temporaryURL)
|
||||
|
||||
completionHandler(.success(temporaryURL))
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Error processing app data:", error)
|
||||
|
||||
completionHandler(.failure(ALTServerError(error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func installApp(at fileURL: URL, toDeviceWithUDID udid: String, activeProvisioningProfiles: Set<String>?, connection: Connection, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
||||
{
|
||||
let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default)
|
||||
var isSending = false
|
||||
|
||||
var observation: NSKeyValueObservation?
|
||||
|
||||
let progress = ALTDeviceManager.shared.installApp(at: fileURL, toDeviceWithUDID: udid, activeProvisioningProfiles: activeProvisioningProfiles) { (success, error) in
|
||||
print("Installed app with result:", error == nil ? "Success" : error!.localizedDescription)
|
||||
|
||||
if let error = error.map({ $0 as? ALTServerError ?? ALTServerError(.unknown) })
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
|
||||
observation?.invalidate()
|
||||
observation = nil
|
||||
}
|
||||
|
||||
observation = progress.observe(\.fractionCompleted, changeHandler: { (progress, change) in
|
||||
serialQueue.async {
|
||||
guard !isSending else { return }
|
||||
isSending = true
|
||||
|
||||
print("Progress:", progress.fractionCompleted)
|
||||
let response = InstallationProgressResponse(progress: progress.fractionCompleted)
|
||||
|
||||
connection.send(response) { (result) in
|
||||
serialQueue.async {
|
||||
isSending = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
116
AltServer/Connections/WiredConnectionHandler.swift
Normal file
116
AltServer/Connections/WiredConnectionHandler.swift
Normal file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// WiredConnectionHandler.swift
|
||||
// AltServer
|
||||
//
|
||||
// Created by Riley Testut on 6/1/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AltKit
|
||||
|
||||
class WiredConnectionHandler: ConnectionHandler
|
||||
{
|
||||
var connectionHandler: ((Connection) -> Void)?
|
||||
var disconnectionHandler: ((Connection) -> Void)?
|
||||
|
||||
private var notificationConnections = [ALTDevice: NotificationConnection]()
|
||||
|
||||
func startListening()
|
||||
{
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(WiredConnectionHandler.deviceDidConnect(_:)), name: .deviceManagerDeviceDidConnect, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(WiredConnectionHandler.deviceDidDisconnect(_:)), name: .deviceManagerDeviceDidDisconnect, object: nil)
|
||||
}
|
||||
|
||||
func stopListening()
|
||||
{
|
||||
NotificationCenter.default.removeObserver(self, name: .deviceManagerDeviceDidConnect, object: nil)
|
||||
NotificationCenter.default.removeObserver(self, name: .deviceManagerDeviceDidDisconnect, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private extension WiredConnectionHandler
|
||||
{
|
||||
func startNotificationConnection(to device: ALTDevice)
|
||||
{
|
||||
ALTDeviceManager.shared.startNotificationConnection(to: device) { (connection, error) in
|
||||
guard let connection = connection else { return }
|
||||
|
||||
let notifications: [CFNotificationName] = [.wiredServerConnectionAvailableRequest, .wiredServerConnectionStartRequest]
|
||||
connection.startListening(forNotifications: notifications.map { String($0.rawValue) }) { (success, error) in
|
||||
guard success else { return }
|
||||
|
||||
connection.receivedNotificationHandler = { [weak self, weak connection] (notification) in
|
||||
guard let self = self, let connection = connection else { return }
|
||||
self.handle(notification, for: connection)
|
||||
}
|
||||
|
||||
self.notificationConnections[device] = connection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopNotificationConnection(to device: ALTDevice)
|
||||
{
|
||||
guard let connection = self.notificationConnections[device] else { return }
|
||||
connection.disconnect()
|
||||
|
||||
self.notificationConnections[device] = nil
|
||||
}
|
||||
|
||||
func handle(_ notification: CFNotificationName, for connection: NotificationConnection)
|
||||
{
|
||||
switch notification
|
||||
{
|
||||
case .wiredServerConnectionAvailableRequest:
|
||||
connection.sendNotification(.wiredServerConnectionAvailableResponse) { (success, error) in
|
||||
if let error = error, !success
|
||||
{
|
||||
print("Error sending wired server connection response.", error)
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Sent wired server connection available response!")
|
||||
}
|
||||
}
|
||||
|
||||
case .wiredServerConnectionStartRequest:
|
||||
ALTDeviceManager.shared.startWiredConnection(to: connection.device) { (wiredConnection, error) in
|
||||
if let wiredConnection = wiredConnection
|
||||
{
|
||||
print("Started wired server connection!")
|
||||
self.connectionHandler?(wiredConnection)
|
||||
|
||||
var observation: NSKeyValueObservation?
|
||||
observation = wiredConnection.observe(\.isConnected) { [weak self] (connection, change) in
|
||||
guard !connection.isConnected else { return }
|
||||
self?.disconnectionHandler?(connection)
|
||||
|
||||
observation?.invalidate()
|
||||
}
|
||||
}
|
||||
else if let error = error
|
||||
{
|
||||
print("Error starting wired server connection.", error)
|
||||
}
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension WiredConnectionHandler
|
||||
{
|
||||
@objc func deviceDidConnect(_ notification: Notification)
|
||||
{
|
||||
guard let device = notification.object as? ALTDevice else { return }
|
||||
self.startNotificationConnection(to: device)
|
||||
}
|
||||
|
||||
@objc func deviceDidDisconnect(_ notification: Notification)
|
||||
{
|
||||
guard let device = notification.object as? ALTDevice else { return }
|
||||
self.stopNotificationConnection(to: device)
|
||||
}
|
||||
}
|
||||
150
AltServer/Connections/WirelessConnectionHandler.swift
Normal file
150
AltServer/Connections/WirelessConnectionHandler.swift
Normal file
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// WirelessConnectionHandler.swift
|
||||
// AltKit
|
||||
//
|
||||
// Created by Riley Testut on 6/1/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
import AltKit
|
||||
|
||||
extension WirelessConnectionHandler
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
case notRunning
|
||||
case connecting
|
||||
case running(NWListener.Service)
|
||||
case failed(Swift.Error)
|
||||
}
|
||||
}
|
||||
|
||||
public class WirelessConnectionHandler: ConnectionHandler
|
||||
{
|
||||
public var connectionHandler: ((Connection) -> Void)?
|
||||
public var disconnectionHandler: ((Connection) -> Void)?
|
||||
|
||||
public var stateUpdateHandler: ((State) -> Void)?
|
||||
|
||||
public private(set) var state: State = .notRunning {
|
||||
didSet {
|
||||
self.stateUpdateHandler?(self.state)
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var listener = self.makeListener()
|
||||
private let dispatchQueue = DispatchQueue(label: "io.altstore.WirelessConnectionListener", qos: .utility)
|
||||
|
||||
public func startListening()
|
||||
{
|
||||
switch self.state
|
||||
{
|
||||
case .notRunning, .failed: self.listener.start(queue: self.dispatchQueue)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
public func stopListening()
|
||||
{
|
||||
switch self.state
|
||||
{
|
||||
case .running: self.listener.cancel()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension WirelessConnectionHandler
|
||||
{
|
||||
func makeListener() -> NWListener
|
||||
{
|
||||
let listener = try! NWListener(using: .tcp)
|
||||
|
||||
let service: NWListener.Service
|
||||
|
||||
if let serverID = UserDefaults.standard.serverID?.data(using: .utf8)
|
||||
{
|
||||
let txtDictionary = ["serverID": serverID]
|
||||
let txtData = NetService.data(fromTXTRecord: txtDictionary)
|
||||
|
||||
service = NWListener.Service(name: nil, type: ALTServerServiceType, domain: nil, txtRecord: txtData)
|
||||
}
|
||||
else
|
||||
{
|
||||
service = NWListener.Service(type: ALTServerServiceType)
|
||||
}
|
||||
|
||||
listener.service = service
|
||||
|
||||
listener.serviceRegistrationUpdateHandler = { (serviceChange) in
|
||||
switch serviceChange
|
||||
{
|
||||
case .add(.service(let name, let type, let domain, _)):
|
||||
let service = NWListener.Service(name: name, type: type, domain: domain, txtRecord: nil)
|
||||
self.state = .running(service)
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
listener.stateUpdateHandler = { (state) in
|
||||
switch state
|
||||
{
|
||||
case .ready: break
|
||||
case .waiting, .setup: self.state = .connecting
|
||||
case .cancelled: self.state = .notRunning
|
||||
case .failed(let error): self.state = .failed(error)
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
listener.newConnectionHandler = { [weak self] (connection) in
|
||||
self?.prepare(connection)
|
||||
}
|
||||
|
||||
return listener
|
||||
}
|
||||
|
||||
func prepare(_ nwConnection: NWConnection)
|
||||
{
|
||||
print("Preparing:", nwConnection)
|
||||
|
||||
// Use same instance for all callbacks.
|
||||
let connection = NetworkConnection(nwConnection)
|
||||
|
||||
nwConnection.stateUpdateHandler = { [weak self] (state) in
|
||||
switch state
|
||||
{
|
||||
case .setup, .preparing: break
|
||||
|
||||
case .ready:
|
||||
print("Connected to client:", connection)
|
||||
self?.connectionHandler?(connection)
|
||||
|
||||
case .waiting:
|
||||
print("Waiting for connection...")
|
||||
|
||||
case .failed(let error):
|
||||
print("Failed to connect to service \(nwConnection.endpoint).", error)
|
||||
self?.disconnect(connection)
|
||||
|
||||
case .cancelled:
|
||||
self?.disconnect(connection)
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
nwConnection.start(queue: self.dispatchQueue)
|
||||
}
|
||||
|
||||
func disconnect(_ connection: Connection)
|
||||
{
|
||||
connection.disconnect()
|
||||
|
||||
self.disconnectionHandler?(connection)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,10 @@
|
||||
BF100C50232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF100C4F232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel */; };
|
||||
BF100C54232D7DAE006A8926 /* StoreAppPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF100C53232D7DAE006A8926 /* StoreAppPolicy.swift */; };
|
||||
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B0F022E25DF9005C4CF5 /* ToastView.swift */; };
|
||||
BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* ConnectionManager.swift */; };
|
||||
BF18BFF32485828200DD5981 /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF22485828200DD5981 /* ConnectionManager.swift */; };
|
||||
BF18BFF724858BDE00DD5981 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF624858BDE00DD5981 /* Connection.swift */; };
|
||||
BF18BFFD2485A1E400DD5981 /* WiredConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */; };
|
||||
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* RequestHandler.swift */; };
|
||||
BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; };
|
||||
BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; };
|
||||
BF1E315A22A0620000370A3C /* NSError+ALTServerError.m in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314922A060F400370A3C /* NSError+ALTServerError.m */; };
|
||||
@@ -130,7 +133,6 @@
|
||||
BF580482246A28F7008AE704 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF580481246A28F7008AE704 /* ViewController.swift */; };
|
||||
BF580487246A28F9008AE704 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF580486246A28F9008AE704 /* Assets.xcassets */; };
|
||||
BF58048A246A28F9008AE704 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF580488246A28F9008AE704 /* LaunchScreen.storyboard */; };
|
||||
BF580492246A2C5C008AE704 /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; };
|
||||
BF580496246A3CB5008AE704 /* UIColor+AltBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF580495246A3CB5008AE704 /* UIColor+AltBackup.swift */; };
|
||||
BF580498246A3D19008AE704 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF580497246A3D19008AE704 /* UIKit.framework */; };
|
||||
BF58049B246A432D008AE704 /* NSError+LocalizedFailure.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+LocalizedFailure.swift */; };
|
||||
@@ -158,7 +160,6 @@
|
||||
BF7C627423DBB78C00515A2D /* InstalledAppPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7C627323DBB78C00515A2D /* InstalledAppPolicy.swift */; };
|
||||
BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; };
|
||||
BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C322E662D300049BA1 /* AppViewController.swift */; };
|
||||
BF9A03C623C7DD0D000D08DB /* ClientConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */; };
|
||||
BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */; };
|
||||
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */; };
|
||||
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */; };
|
||||
@@ -262,6 +263,9 @@
|
||||
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; };
|
||||
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */; };
|
||||
BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */; };
|
||||
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
|
||||
BFF767CC2489AB5C0097E58C /* ALTServerError+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */; };
|
||||
BFF767CE2489ABE90097E58C /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CD2489ABE90097E58C /* NetworkConnection.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -346,8 +350,12 @@
|
||||
BF100C4F232D7CD1006A8926 /* AltStoreToAltStore2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStoreToAltStore2.xcmappingmodel; sourceTree = "<group>"; };
|
||||
BF100C53232D7DAE006A8926 /* StoreAppPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreAppPolicy.swift; sourceTree = "<group>"; };
|
||||
BF18B0F022E25DF9005C4CF5 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
||||
BF18BFF22485828200DD5981 /* ConnectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = "<group>"; };
|
||||
BF18BFF624858BDE00DD5981 /* Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = "<group>"; };
|
||||
BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiredConnectionHandler.swift; sourceTree = "<group>"; };
|
||||
BF18BFFE2485A42800DD5981 /* ALTConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTConnection.h; sourceTree = "<group>"; };
|
||||
BF1E3128229F474900370A3C /* ServerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerProtocol.swift; sourceTree = "<group>"; };
|
||||
BF1E3129229F474900370A3C /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = "<group>"; };
|
||||
BF1E3129229F474900370A3C /* RequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestHandler.swift; sourceTree = "<group>"; };
|
||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AltStore.swift"; sourceTree = "<group>"; };
|
||||
BF1E314722A060F300370A3C /* AltStore-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AltStore-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
BF1E314822A060F400370A3C /* NSError+ALTServerError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSError+ALTServerError.h"; sourceTree = "<group>"; };
|
||||
@@ -504,7 +512,6 @@
|
||||
BF7C627323DBB78C00515A2D /* InstalledAppPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledAppPolicy.swift; sourceTree = "<group>"; };
|
||||
BF8F69C122E659F700049BA1 /* AppContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewController.swift; sourceTree = "<group>"; };
|
||||
BF8F69C322E662D300049BA1 /* AppViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = "<group>"; };
|
||||
BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientConnection.swift; sourceTree = "<group>"; };
|
||||
BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseViewController.swift; sourceTree = "<group>"; };
|
||||
BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
@@ -617,6 +624,9 @@
|
||||
BFF0B695232242D3007A79E1 /* LicensesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesViewController.swift; sourceTree = "<group>"; };
|
||||
BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsViewController.swift; sourceTree = "<group>"; };
|
||||
BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+CompactHeight.swift"; sourceTree = "<group>"; };
|
||||
BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WirelessConnectionHandler.swift; sourceTree = "<group>"; };
|
||||
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTServerError+Conveniences.swift"; sourceTree = "<group>"; };
|
||||
BFF767CD2489ABE90097E58C /* NetworkConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; };
|
||||
EA79A60285C6AF5848AA16E9 /* Pods-AltStore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStore.debug.xcconfig"; path = "Target Support Files/Pods-AltStore/Pods-AltStore.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
FC3822AB1C4CF1D4CDF7445D /* Pods_AltServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltServer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -735,19 +745,24 @@
|
||||
path = Policies;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF18BFFF2485A75F00DD5981 /* Server Protocol */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF1E3128229F474900370A3C /* ServerProtocol.swift */,
|
||||
BFD44605241188C300EAB90A /* CodableServerError.swift */,
|
||||
);
|
||||
path = "Server Protocol";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF1E315122A0616100370A3C /* AltKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFD52BD222A06EFB000B7ED1 /* AltKit.h */,
|
||||
BF718BD723C93DB700A89F2D /* AltKit.m */,
|
||||
BFBAC8852295C90300587369 /* Result+Conveniences.swift */,
|
||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
||||
BF1E3128229F474900370A3C /* ServerProtocol.swift */,
|
||||
BFD44605241188C300EAB90A /* CodableServerError.swift */,
|
||||
BF1E314822A060F400370A3C /* NSError+ALTServerError.h */,
|
||||
BF1E314922A060F400370A3C /* NSError+ALTServerError.m */,
|
||||
BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */,
|
||||
BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */,
|
||||
BF18BFFF2485A75F00DD5981 /* Server Protocol */,
|
||||
BFF767CF2489AC240097E58C /* Connections */,
|
||||
BFF767C32489A6800097E58C /* Extensions */,
|
||||
BFF767C42489A6980097E58C /* Categories */,
|
||||
);
|
||||
path = AltKit;
|
||||
sourceTree = "<group>";
|
||||
@@ -1232,8 +1247,9 @@
|
||||
BFD52BDC22A0A659000B7ED1 /* Connections */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF1E3129229F474900370A3C /* ConnectionManager.swift */,
|
||||
BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */,
|
||||
BF1E3129229F474900370A3C /* RequestHandler.swift */,
|
||||
BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */,
|
||||
BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */,
|
||||
BF718BCF23C91BD300A89F2D /* ALTWiredConnection.h */,
|
||||
BF718BD223C91C7000A89F2D /* ALTWiredConnection+Private.h */,
|
||||
BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */,
|
||||
@@ -1320,6 +1336,38 @@
|
||||
path = Authentication;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFF767C32489A6800097E58C /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFBAC8852295C90300587369 /* Result+Conveniences.swift */,
|
||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
||||
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFF767C42489A6980097E58C /* Categories */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF1E314822A060F400370A3C /* NSError+ALTServerError.h */,
|
||||
BF1E314922A060F400370A3C /* NSError+ALTServerError.m */,
|
||||
BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */,
|
||||
BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFF767CF2489AC240097E58C /* Connections */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF18BFF22485828200DD5981 /* ConnectionManager.swift */,
|
||||
BF18BFFE2485A42800DD5981 /* ALTConnection.h */,
|
||||
BF18BFF624858BDE00DD5981 /* Connection.swift */,
|
||||
BFF767CD2489ABE90097E58C /* NetworkConnection.swift */,
|
||||
);
|
||||
path = Connections;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -1729,10 +1777,14 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BF718BD823C93DB700A89F2D /* AltKit.m in Sources */,
|
||||
BFF767CE2489ABE90097E58C /* NetworkConnection.swift in Sources */,
|
||||
BFF767CC2489AB5C0097E58C /* ALTServerError+Conveniences.swift in Sources */,
|
||||
BF1E315A22A0620000370A3C /* NSError+ALTServerError.m in Sources */,
|
||||
BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */,
|
||||
BF718BC923C919E300A89F2D /* CFNotificationName+AltStore.m in Sources */,
|
||||
BFD44606241188C400EAB90A /* CodableServerError.swift in Sources */,
|
||||
BF18BFF32485828200DD5981 /* ConnectionManager.swift in Sources */,
|
||||
BF18BFF724858BDE00DD5981 /* Connection.swift in Sources */,
|
||||
BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */,
|
||||
BF4E8456246F16D700ECCBD4 /* Bundle+AltStore.swift in Sources */,
|
||||
);
|
||||
@@ -1742,10 +1794,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */,
|
||||
BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */,
|
||||
BF18BFFD2485A1E400DD5981 /* WiredConnectionHandler.swift in Sources */,
|
||||
BF718BD523C928A300A89F2D /* ALTNotificationConnection.m in Sources */,
|
||||
BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */,
|
||||
BF9A03C623C7DD0D000D08DB /* ClientConnection.swift in Sources */,
|
||||
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */,
|
||||
BF718BD123C91BD300A89F2D /* ALTWiredConnection.m in Sources */,
|
||||
BF458690229872EA00BD7491 /* AppDelegate.swift in Sources */,
|
||||
BF4586C52298CDB800BD7491 /* ALTDeviceManager.mm in Sources */,
|
||||
@@ -1819,7 +1872,6 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BF580492246A2C5C008AE704 /* Bundle+AltStore.swift in Sources */,
|
||||
BF580496246A3CB5008AE704 /* UIColor+AltBackup.swift in Sources */,
|
||||
BF580482246A28F7008AE704 /* ViewController.swift in Sources */,
|
||||
BF44EEF0246B08BA002A52F2 /* BackupController.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user