mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-19 03:33:36 +01:00
[Both] Adds support for installing apps over USB
This commit is contained in:
@@ -7,3 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "NSError+ALTServerError.h"
|
#import "NSError+ALTServerError.h"
|
||||||
|
#import "CFNotificationName+AltStore.h"
|
||||||
|
|
||||||
|
extern uint16_t ALTDeviceListeningSocket;
|
||||||
|
|||||||
11
AltKit/AltKit.m
Normal file
11
AltKit/AltKit.m
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// AltKit.m
|
||||||
|
// AltKit
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
uint16_t ALTDeviceListeningSocket = 28151;
|
||||||
17
AltKit/CFNotificationName+AltStore.h
Normal file
17
AltKit/CFNotificationName+AltStore.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// CFNotificationName+AltStore.h
|
||||||
|
// AltKit
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
extern CFNotificationName const ALTWiredServerConnectionAvailableRequest NS_SWIFT_NAME(wiredServerConnectionAvailableRequest);
|
||||||
|
extern CFNotificationName const ALTWiredServerConnectionAvailableResponse NS_SWIFT_NAME(wiredServerConnectionAvailableResponse);
|
||||||
|
extern CFNotificationName const ALTWiredServerConnectionStartRequest NS_SWIFT_NAME(wiredServerConnectionStartRequest);
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
13
AltKit/CFNotificationName+AltStore.m
Normal file
13
AltKit/CFNotificationName+AltStore.m
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// CFNotificationName+AltStore.m
|
||||||
|
// AltKit
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "CFNotificationName+AltStore.h"
|
||||||
|
|
||||||
|
CFNotificationName const ALTWiredServerConnectionAvailableRequest = CFSTR("io.altstore.Request.WiredServerConnectionAvailable");
|
||||||
|
CFNotificationName const ALTWiredServerConnectionAvailableResponse = CFSTR("io.altstore.Response.WiredServerConnectionAvailable");
|
||||||
|
CFNotificationName const ALTWiredServerConnectionStartRequest = CFSTR("io.altstore.Request.WiredServerConnectionStart");
|
||||||
@@ -3,3 +3,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "ALTDeviceManager.h"
|
#import "ALTDeviceManager.h"
|
||||||
|
#import "ALTWiredConnection.h"
|
||||||
|
#import "ALTNotificationConnection.h"
|
||||||
|
#import "AltKit.h"
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
UserDefaults.standard.registerDefaults()
|
UserDefaults.standard.registerDefaults()
|
||||||
|
|
||||||
UNUserNotificationCenter.current().delegate = self
|
UNUserNotificationCenter.current().delegate = self
|
||||||
|
|
||||||
ConnectionManager.shared.start()
|
ConnectionManager.shared.start()
|
||||||
|
ALTDeviceManager.shared.start()
|
||||||
|
|
||||||
let item = NSStatusBar.system.statusItem(withLength: -1)
|
let item = NSStatusBar.system.statusItem(withLength: -1)
|
||||||
guard let button = item.button else { return }
|
guard let button = item.button else { return }
|
||||||
|
|||||||
24
AltServer/Connections/ALTNotificationConnection+Private.h
Normal file
24
AltServer/Connections/ALTNotificationConnection+Private.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// ALTNotificationConnection+Private.h
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ALTNotificationConnection.h"
|
||||||
|
|
||||||
|
#include <libimobiledevice/libimobiledevice.h>
|
||||||
|
#include <libimobiledevice/notification_proxy.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ALTNotificationConnection ()
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) np_client_t client;
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(ALTDevice *)device client:(np_client_t)client;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
30
AltServer/Connections/ALTNotificationConnection.h
Normal file
30
AltServer/Connections/ALTNotificationConnection.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// ALTNotificationConnection.h
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AltSign/AltSign.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
NS_SWIFT_NAME(NotificationConnection)
|
||||||
|
@interface ALTNotificationConnection : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, copy, readonly) ALTDevice *device;
|
||||||
|
|
||||||
|
@property (nonatomic, copy, nullable) void (^receivedNotificationHandler)(CFNotificationName notification);
|
||||||
|
|
||||||
|
- (void)startListeningForNotifications:(NSArray<NSString *> *)notifications
|
||||||
|
completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
|
|
||||||
|
- (void)sendNotification:(CFNotificationName)notification
|
||||||
|
completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
|
|
||||||
|
- (void)disconnect;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
93
AltServer/Connections/ALTNotificationConnection.m
Normal file
93
AltServer/Connections/ALTNotificationConnection.m
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
//
|
||||||
|
// ALTNotificationConnection.m
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ALTNotificationConnection+Private.h"
|
||||||
|
#import "AltKit.h"
|
||||||
|
|
||||||
|
void ALTDeviceReceivedNotification(const char *notification, void *user_data);
|
||||||
|
|
||||||
|
@implementation ALTNotificationConnection
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(ALTDevice *)device client:(np_client_t)client
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_device = [device copy];
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disconnect
|
||||||
|
{
|
||||||
|
np_client_free(self.client);
|
||||||
|
_client = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startListeningForNotifications:(NSArray<NSString *> *)notifications completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
|
||||||
|
{
|
||||||
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
||||||
|
const char **notificationNames = (const char **)malloc((notifications.count + 1) * sizeof(char *));
|
||||||
|
for (int i = 0; i < notifications.count; i++)
|
||||||
|
{
|
||||||
|
NSString *name = notifications[i];
|
||||||
|
notificationNames[i] = name.UTF8String;
|
||||||
|
}
|
||||||
|
notificationNames[notifications.count] = NULL; // Must have terminating NULL entry.
|
||||||
|
|
||||||
|
np_error_t result = np_observe_notifications(self.client, notificationNames);
|
||||||
|
if (result != NP_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return completionHandler(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = np_set_notify_callback(self.client, ALTDeviceReceivedNotification, (__bridge void *)self);
|
||||||
|
if (result != NP_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return completionHandler(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(YES, nil);
|
||||||
|
|
||||||
|
free(notificationNames);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendNotification:(CFNotificationName)notification completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
|
||||||
|
{
|
||||||
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
||||||
|
np_error_t result = np_post_notification(self.client, [(__bridge NSString *)notification UTF8String]);
|
||||||
|
if (result == NP_E_SUCCESS)
|
||||||
|
{
|
||||||
|
completionHandler(YES, nil);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completionHandler(NO, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
void ALTDeviceReceivedNotification(const char *notification, void *user_data)
|
||||||
|
{
|
||||||
|
ALTNotificationConnection *connection = (__bridge ALTNotificationConnection *)user_data;
|
||||||
|
|
||||||
|
if (connection.receivedNotificationHandler)
|
||||||
|
{
|
||||||
|
connection.receivedNotificationHandler((__bridge CFNotificationName)@(notification));
|
||||||
|
}
|
||||||
|
}
|
||||||
23
AltServer/Connections/ALTWiredConnection+Private.h
Normal file
23
AltServer/Connections/ALTWiredConnection+Private.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// ALTWiredConnection+Private.h
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ALTWiredConnection.h"
|
||||||
|
|
||||||
|
#include <libimobiledevice/libimobiledevice.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ALTWiredConnection ()
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) idevice_connection_t connection;
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(ALTDevice *)device connection:(idevice_connection_t)connection;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
25
AltServer/Connections/ALTWiredConnection.h
Normal file
25
AltServer/Connections/ALTWiredConnection.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// ALTWiredConnection.h
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AltSign/AltSign.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
NS_SWIFT_NAME(WiredConnection)
|
||||||
|
@interface ALTWiredConnection : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, copy, readonly) ALTDevice *device;
|
||||||
|
|
||||||
|
- (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler;
|
||||||
|
- (void)receiveDataWithExpectedSize:(NSInteger)expectedSize completionHandler:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler;
|
||||||
|
|
||||||
|
- (void)disconnect;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
101
AltServer/Connections/ALTWiredConnection.m
Normal file
101
AltServer/Connections/ALTWiredConnection.m
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// ALTWiredConnection.m
|
||||||
|
// AltServer
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/10/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ALTWiredConnection+Private.h"
|
||||||
|
#import "AltKit.h"
|
||||||
|
|
||||||
|
@implementation ALTWiredConnection
|
||||||
|
|
||||||
|
- (instancetype)initWithDevice:(ALTDevice *)device connection:(idevice_connection_t)connection
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
_device = [device copy];
|
||||||
|
_connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disconnect
|
||||||
|
{
|
||||||
|
idevice_disconnect(self.connection);
|
||||||
|
_connection = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendData:(NSData *)data completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
|
||||||
|
{
|
||||||
|
void (^finish)(NSError *error) = ^(NSError *error) {
|
||||||
|
if (error != nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Send Error: %@", error);
|
||||||
|
completionHandler(NO, error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completionHandler(YES, nil);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
||||||
|
NSMutableData *mutableData = [data mutableCopy];
|
||||||
|
while (mutableData.length > 0)
|
||||||
|
{
|
||||||
|
uint32_t sentBytes = 0;
|
||||||
|
if (idevice_connection_send(self.connection, (const char *)mutableData.bytes, (int32_t)mutableData.length, &sentBytes) != IDEVICE_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish([NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[mutableData replaceBytesInRange:NSMakeRange(0, sentBytes) withBytes:NULL length:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
finish(nil);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)receiveDataWithExpectedSize:(NSInteger)expectedSize completionHandler:(void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler
|
||||||
|
{
|
||||||
|
void (^finish)(NSData *data, NSError *error) = ^(NSData *data, NSError *error) {
|
||||||
|
if (error != nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Receive Data Error: %@", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(data, error);
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
||||||
|
char bytes[4096];
|
||||||
|
NSMutableData *receivedData = [NSMutableData dataWithCapacity:expectedSize];
|
||||||
|
|
||||||
|
while (receivedData.length < expectedSize)
|
||||||
|
{
|
||||||
|
uint32_t size = MIN(4096, (uint32_t)expectedSize - (uint32_t)receivedData.length);
|
||||||
|
|
||||||
|
uint32_t receivedBytes = 0;
|
||||||
|
if (idevice_connection_receive(self.connection, bytes, size, &receivedBytes) != IDEVICE_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorLostConnection userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *data = [NSData dataWithBytesNoCopy:bytes length:receivedBytes freeWhenDone:NO];
|
||||||
|
[receivedData appendData:data];
|
||||||
|
}
|
||||||
|
|
||||||
|
finish(receivedData, nil);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
231
AltServer/Connections/ClientConnection.swift
Normal file
231
AltServer/Connections/ClientConnection.swift
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
//
|
||||||
|
// 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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,10 +54,13 @@ class ConnectionManager
|
|||||||
private lazy var listener = self.makeListener()
|
private lazy var listener = self.makeListener()
|
||||||
private let dispatchQueue = DispatchQueue(label: "com.rileytestut.AltServer.connections", qos: .utility)
|
private let dispatchQueue = DispatchQueue(label: "com.rileytestut.AltServer.connections", qos: .utility)
|
||||||
|
|
||||||
private var connections = [NWConnection]()
|
private var connections = [ClientConnection]()
|
||||||
|
private var notificationConnections = [ALTDevice: NotificationConnection]()
|
||||||
|
|
||||||
private init()
|
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()
|
func start()
|
||||||
@@ -77,6 +80,16 @@ class ConnectionManager
|
|||||||
default: break
|
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
|
private extension ConnectionManager
|
||||||
@@ -127,67 +140,18 @@ private extension ConnectionManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
listener.newConnectionHandler = { [weak self] (connection) in
|
listener.newConnectionHandler = { [weak self] (connection) in
|
||||||
self?.awaitRequest(from: connection)
|
self?.prepare(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
return listener
|
return listener
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect(_ connection: NWConnection)
|
func prepare(_ connection: NWConnection)
|
||||||
{
|
{
|
||||||
switch connection.state
|
let clientConnection = ClientConnection(connection: .wireless(connection))
|
||||||
{
|
|
||||||
case .cancelled, .failed:
|
guard !self.connections.contains(where: { $0 === clientConnection }) else { return }
|
||||||
print("Disconnecting from \(connection.endpoint)...")
|
self.connections.append(clientConnection)
|
||||||
|
|
||||||
if let index = self.connections.firstIndex(where: { $0 === connection })
|
|
||||||
{
|
|
||||||
self.connections.remove(at: index)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// State update handler will call this method again.
|
|
||||||
connection.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func process(data: Data?, error: NWError?, from connection: NWConnection) 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.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension ConnectionManager
|
|
||||||
{
|
|
||||||
func awaitRequest(from connection: NWConnection)
|
|
||||||
{
|
|
||||||
guard !self.connections.contains(where: { $0 === connection }) else { return }
|
|
||||||
self.connections.append(connection)
|
|
||||||
|
|
||||||
connection.stateUpdateHandler = { [weak self] (state) in
|
connection.stateUpdateHandler = { [weak self] (state) in
|
||||||
switch state
|
switch state
|
||||||
@@ -196,17 +160,17 @@ private extension ConnectionManager
|
|||||||
|
|
||||||
case .ready:
|
case .ready:
|
||||||
print("Connected to client:", connection.endpoint)
|
print("Connected to client:", connection.endpoint)
|
||||||
self?.handleRequest(for: connection)
|
self?.handleRequest(for: clientConnection)
|
||||||
|
|
||||||
case .waiting:
|
case .waiting:
|
||||||
print("Waiting for connection...")
|
print("Waiting for connection...")
|
||||||
|
|
||||||
case .failed(let error):
|
case .failed(let error):
|
||||||
print("Failed to connect to service \(connection.endpoint).", error)
|
print("Failed to connect to service \(connection.endpoint).", error)
|
||||||
self?.disconnect(connection)
|
self?.disconnect(clientConnection)
|
||||||
|
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
self?.disconnect(connection)
|
self?.disconnect(clientConnection)
|
||||||
|
|
||||||
@unknown default: break
|
@unknown default: break
|
||||||
}
|
}
|
||||||
@@ -214,17 +178,85 @@ private extension ConnectionManager
|
|||||||
|
|
||||||
connection.start(queue: self.dispatchQueue)
|
connection.start(queue: self.dispatchQueue)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
func handleRequest(for connection: NWConnection)
|
|
||||||
|
private extension ConnectionManager
|
||||||
|
{
|
||||||
|
func startNotificationConnection(to device: ALTDevice)
|
||||||
{
|
{
|
||||||
self.receiveRequest(from: connection) { (result) in
|
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)
|
print("Received initial request with result:", result)
|
||||||
|
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let response = ErrorResponse(error: ALTServerError(error))
|
let response = ErrorResponse(error: ALTServerError(error))
|
||||||
self.send(response, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent error response with result:", result)
|
print("Sent error response with result:", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,34 +268,34 @@ private extension ConnectionManager
|
|||||||
|
|
||||||
case .success:
|
case .success:
|
||||||
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
||||||
self.send(response, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent unknown request response with result:", result)
|
print("Sent unknown request response with result:", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: NWConnection)
|
func handleAnisetteDataRequest(_ request: AnisetteDataRequest, for connection: ClientConnection)
|
||||||
{
|
{
|
||||||
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
let errorResponse = ErrorResponse(error: ALTServerError(error))
|
||||||
self.send(errorResponse, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(errorResponse, shouldDisconnect: true) { (result) in
|
||||||
print("Sent anisette data error response with result:", result)
|
print("Sent anisette data error response with result:", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .success(let anisetteData):
|
case .success(let anisetteData):
|
||||||
let response = AnisetteDataResponse(anisetteData: anisetteData)
|
let response = AnisetteDataResponse(anisetteData: anisetteData)
|
||||||
self.send(response, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent anisette data response with result:", result)
|
print("Sent anisette data response with result:", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: NWConnection)
|
func handlePrepareAppRequest(_ request: PrepareAppRequest, for connection: ClientConnection)
|
||||||
{
|
{
|
||||||
var temporaryURL: URL?
|
var temporaryURL: URL?
|
||||||
|
|
||||||
@@ -278,19 +310,19 @@ private extension ConnectionManager
|
|||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
print("Failed to process request from \(connection.endpoint).", error)
|
print("Failed to process request from \(connection).", error)
|
||||||
|
|
||||||
let response = ErrorResponse(error: ALTServerError(error))
|
let response = ErrorResponse(error: ALTServerError(error))
|
||||||
self.send(response, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent install app error response to \(connection.endpoint) with result:", result)
|
print("Sent install app error response to \(connection) with result:", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .success:
|
case .success:
|
||||||
print("Processed request from \(connection.endpoint).")
|
print("Processed request from \(connection).")
|
||||||
|
|
||||||
let response = InstallationProgressResponse(progress: 1.0)
|
let response = InstallationProgressResponse(progress: 1.0)
|
||||||
self.send(response, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent install app response to \(connection.endpoint) with result:", result)
|
print("Sent install app response to \(connection) with result:", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,7 +338,7 @@ private extension ConnectionManager
|
|||||||
|
|
||||||
print("Awaiting begin installation request...")
|
print("Awaiting begin installation request...")
|
||||||
|
|
||||||
self.receiveRequest(from: connection) { (result) in
|
connection.receiveRequest() { (result) in
|
||||||
print("Received begin installation request with result:", result)
|
print("Received begin installation request with result:", result)
|
||||||
|
|
||||||
switch result
|
switch result
|
||||||
@@ -326,8 +358,8 @@ private extension ConnectionManager
|
|||||||
|
|
||||||
case .success:
|
case .success:
|
||||||
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
let response = ErrorResponse(error: ALTServerError(.unknownRequest))
|
||||||
self.send(response, to: connection, shouldDisconnect: true) { (result) in
|
connection.send(response, shouldDisconnect: true) { (result) in
|
||||||
print("Sent unknown request error response to \(connection.endpoint) with result:", result)
|
print("Sent unknown request error response to \(connection) with result:", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,17 +367,15 @@ private extension ConnectionManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func receiveApp(for request: PrepareAppRequest, from connection: NWConnection, completionHandler: @escaping (Result<URL, ALTServerError>) -> Void)
|
func receiveApp(for request: PrepareAppRequest, from connection: ClientConnection, completionHandler: @escaping (Result<URL, ALTServerError>) -> Void)
|
||||||
{
|
{
|
||||||
connection.receive(minimumIncompleteLength: request.contentSize, maximumLength: request.contentSize) { (data, _, _, error) in
|
connection.receiveData(expectedBytes: request.contentSize) { (result) in
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
print("Received app data!")
|
print("Received app data!")
|
||||||
|
|
||||||
let data = try self.process(data: data, error: error, from: connection)
|
let data = try result.get()
|
||||||
|
|
||||||
print("Processed app data!")
|
|
||||||
|
|
||||||
guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) }
|
guard ALTDeviceManager.shared.availableDevices.contains(where: { $0.identifier == request.udid }) else { throw ALTServerError(.deviceNotFound) }
|
||||||
|
|
||||||
print("Writing app data...")
|
print("Writing app data...")
|
||||||
@@ -366,7 +396,7 @@ private extension ConnectionManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func installApp(at fileURL: URL, toDeviceWithUDID udid: String, connection: NWConnection, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
func installApp(at fileURL: URL, toDeviceWithUDID udid: String, connection: ClientConnection, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
||||||
{
|
{
|
||||||
let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default)
|
let serialQueue = DispatchQueue(label: "com.altstore.ConnectionManager.installQueue", qos: .default)
|
||||||
var isSending = false
|
var isSending = false
|
||||||
@@ -397,7 +427,7 @@ private extension ConnectionManager
|
|||||||
print("Progress:", progress.fractionCompleted)
|
print("Progress:", progress.fractionCompleted)
|
||||||
let response = InstallationProgressResponse(progress: progress.fractionCompleted)
|
let response = InstallationProgressResponse(progress: progress.fractionCompleted)
|
||||||
|
|
||||||
self.send(response, to: connection) { (result) in
|
connection.send(response) { (result) in
|
||||||
serialQueue.async {
|
serialQueue.async {
|
||||||
isSending = false
|
isSending = false
|
||||||
}
|
}
|
||||||
@@ -405,92 +435,19 @@ private extension ConnectionManager
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func send<T: Encodable>(_ response: T, to connection: NWConnection, shouldDisconnect: Bool = false, completionHandler: @escaping (Result<Void, ALTServerError>) -> Void)
|
private extension ConnectionManager
|
||||||
|
{
|
||||||
|
@objc func deviceDidConnect(_ notification: Notification)
|
||||||
{
|
{
|
||||||
func finish(_ result: Result<Void, ALTServerError>)
|
guard let device = notification.object as? ALTDevice else { return }
|
||||||
{
|
self.startNotificationConnection(to: device)
|
||||||
completionHandler(result)
|
|
||||||
|
|
||||||
if shouldDisconnect
|
|
||||||
{
|
|
||||||
// Add short delay to prevent us from dropping connection too quickly.
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
|
|
||||||
self.disconnect(connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let data = try JSONEncoder().encode(response)
|
|
||||||
let responseSize = withUnsafeBytes(of: Int32(data.count)) { Data($0) }
|
|
||||||
|
|
||||||
connection.send(content: responseSize, completion: .contentProcessed { (error) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if let error = error
|
|
||||||
{
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.send(content: data, completion: .contentProcessed { (error) in
|
|
||||||
if error != nil
|
|
||||||
{
|
|
||||||
finish(.failure(.init(.lostConnection)))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
finish(.success(()))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
finish(.failure(.init(.lostConnection)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
finish(.failure(.init(.invalidResponse)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func receiveRequest(from connection: NWConnection, completionHandler: @escaping (Result<ServerRequest, ALTServerError>) -> Void)
|
@objc func deviceDidDisconnect(_ notification: Notification)
|
||||||
{
|
{
|
||||||
let size = MemoryLayout<Int32>.size
|
guard let device = notification.object as? ALTDevice else { return }
|
||||||
|
self.stopNotificationConnection(to: device)
|
||||||
print("Receiving request size")
|
|
||||||
connection.receive(minimumIncompleteLength: size, maximumLength: size) { (data, _, _, error) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let data = try self.process(data: data, error: error, from: connection)
|
|
||||||
|
|
||||||
print("Receiving request...")
|
|
||||||
|
|
||||||
let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
|
||||||
connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let data = try self.process(data: data, error: error, from: connection)
|
|
||||||
|
|
||||||
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)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,14 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <AltSign/AltSign.h>
|
#import <AltSign/AltSign.h>
|
||||||
|
|
||||||
|
@class ALTWiredConnection;
|
||||||
|
@class ALTNotificationConnection;
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
extern NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification NS_SWIFT_NAME(deviceManagerDeviceDidConnect);
|
||||||
|
extern NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification NS_SWIFT_NAME(deviceManagerDeviceDidDisconnect);
|
||||||
|
|
||||||
@interface ALTDeviceManager : NSObject
|
@interface ALTDeviceManager : NSObject
|
||||||
|
|
||||||
@property (class, nonatomic, readonly) ALTDeviceManager *sharedManager;
|
@property (class, nonatomic, readonly) ALTDeviceManager *sharedManager;
|
||||||
@@ -18,8 +24,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property (nonatomic, readonly) NSArray<ALTDevice *> *connectedDevices;
|
@property (nonatomic, readonly) NSArray<ALTDevice *> *connectedDevices;
|
||||||
@property (nonatomic, readonly) NSArray<ALTDevice *> *availableDevices;
|
@property (nonatomic, readonly) NSArray<ALTDevice *> *availableDevices;
|
||||||
|
|
||||||
|
- (void)start;
|
||||||
|
|
||||||
|
/* App Installation */
|
||||||
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL success, NSError *_Nullable error))completionHandler;
|
||||||
|
|
||||||
|
/* Connections */
|
||||||
|
- (void)startWiredConnectionToDevice:(ALTDevice *)device completionHandler:(void (^)(ALTWiredConnection *_Nullable connection, NSError *_Nullable error))completionHandler;
|
||||||
|
- (void)startNotificationConnectionToDevice:(ALTDevice *)device completionHandler:(void (^)(ALTNotificationConnection *_Nullable connection, NSError *_Nullable error))completionHandler;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "ALTDeviceManager.h"
|
#import "ALTDeviceManager.h"
|
||||||
#import "NSError+ALTServerError.h"
|
|
||||||
|
#import "AltKit.h"
|
||||||
|
#import "ALTWiredConnection+Private.h"
|
||||||
|
#import "ALTNotificationConnection+Private.h"
|
||||||
|
|
||||||
#include <libimobiledevice/libimobiledevice.h>
|
#include <libimobiledevice/libimobiledevice.h>
|
||||||
#include <libimobiledevice/lockdown.h>
|
#include <libimobiledevice/lockdown.h>
|
||||||
@@ -17,8 +20,10 @@
|
|||||||
#include <libimobiledevice/misagent.h>
|
#include <libimobiledevice/misagent.h>
|
||||||
|
|
||||||
void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid);
|
void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid);
|
||||||
|
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data);
|
||||||
|
|
||||||
NSErrorDomain const ALTDeviceErrorDomain = @"com.rileytestut.ALTDeviceError";
|
NSNotificationName const ALTDeviceManagerDeviceDidConnectNotification = @"ALTDeviceManagerDeviceDidConnectNotification";
|
||||||
|
NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALTDeviceManagerDeviceDidDisconnectNotification";
|
||||||
|
|
||||||
@interface ALTDeviceManager ()
|
@interface ALTDeviceManager ()
|
||||||
|
|
||||||
@@ -26,6 +31,8 @@ NSErrorDomain const ALTDeviceErrorDomain = @"com.rileytestut.ALTDeviceError";
|
|||||||
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, NSProgress *> *installationProgress;
|
@property (nonatomic, readonly) NSMutableDictionary<NSUUID *, NSProgress *> *installationProgress;
|
||||||
@property (nonatomic, readonly) dispatch_queue_t installationQueue;
|
@property (nonatomic, readonly) dispatch_queue_t installationQueue;
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) NSMutableSet<ALTDevice *> *cachedDevices;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ALTDeviceManager
|
@implementation ALTDeviceManager
|
||||||
@@ -50,11 +57,20 @@ NSErrorDomain const ALTDeviceErrorDomain = @"com.rileytestut.ALTDeviceError";
|
|||||||
_installationProgress = [NSMutableDictionary dictionary];
|
_installationProgress = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
_installationQueue = dispatch_queue_create("com.rileytestut.AltServer.InstallationQueue", DISPATCH_QUEUE_SERIAL);
|
_installationQueue = dispatch_queue_create("com.rileytestut.AltServer.InstallationQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
|
_cachedDevices = [NSMutableSet set];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)start
|
||||||
|
{
|
||||||
|
idevice_event_subscribe(ALTDeviceDidChangeConnectionStatus, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - App Installation -
|
||||||
|
|
||||||
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
|
- (NSProgress *)installAppAtURL:(NSURL *)fileURL toDeviceWithUDID:(NSString *)udid completionHandler:(void (^)(BOOL, NSError * _Nullable))completionHandler
|
||||||
{
|
{
|
||||||
NSProgress *progress = [NSProgress discreteProgressWithTotalUnitCount:4];
|
NSProgress *progress = [NSProgress discreteProgressWithTotalUnitCount:4];
|
||||||
@@ -527,6 +543,89 @@ NSErrorDomain const ALTDeviceErrorDomain = @"com.rileytestut.ALTDeviceError";
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Connections -
|
||||||
|
|
||||||
|
- (void)startWiredConnectionToDevice:(ALTDevice *)altDevice completionHandler:(void (^)(ALTWiredConnection * _Nullable, NSError * _Nullable))completionHandler
|
||||||
|
{
|
||||||
|
void (^finish)(ALTWiredConnection *connection, NSError *error) = ^(ALTWiredConnection *connection, NSError *error) {
|
||||||
|
if (error != nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Wired Connection Error: %@", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(connection, error);
|
||||||
|
};
|
||||||
|
|
||||||
|
idevice_t device = NULL;
|
||||||
|
idevice_connection_t connection = NULL;
|
||||||
|
|
||||||
|
/* Find Device */
|
||||||
|
if (idevice_new_ignore_network(&device, altDevice.identifier.UTF8String) != IDEVICE_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Listening Socket */
|
||||||
|
if (idevice_connect(device, ALTDeviceListeningSocket, &connection) != IDEVICE_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
idevice_free(device);
|
||||||
|
|
||||||
|
ALTWiredConnection *wiredConnection = [[ALTWiredConnection alloc] initWithDevice:altDevice connection:connection];
|
||||||
|
finish(wiredConnection, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startNotificationConnectionToDevice:(ALTDevice *)altDevice completionHandler:(void (^)(ALTNotificationConnection * _Nullable, NSError * _Nullable))completionHandler
|
||||||
|
{
|
||||||
|
void (^finish)(ALTNotificationConnection *, NSError *) = ^(ALTNotificationConnection *connection, NSError *error) {
|
||||||
|
if (error != nil)
|
||||||
|
{
|
||||||
|
NSLog(@"Notification Connection Error: %@", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(connection, error);
|
||||||
|
};
|
||||||
|
|
||||||
|
idevice_t device = NULL;
|
||||||
|
lockdownd_client_t lockdownClient = NULL;
|
||||||
|
lockdownd_service_descriptor_t service = NULL;
|
||||||
|
|
||||||
|
np_client_t client = NULL;
|
||||||
|
|
||||||
|
/* Find Device */
|
||||||
|
if (idevice_new_ignore_network(&device, altDevice.identifier.UTF8String) != IDEVICE_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorDeviceNotFound userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Device */
|
||||||
|
if (lockdownd_client_new_with_handshake(device, &lockdownClient, "altserver") != LOCKDOWN_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Notification Proxy */
|
||||||
|
if ((lockdownd_start_service(lockdownClient, "com.apple.mobile.notification_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Client */
|
||||||
|
if (np_client_new(device, service, &client) != NP_E_SUCCESS)
|
||||||
|
{
|
||||||
|
return finish(nil, [NSError errorWithDomain:AltServerErrorDomain code:ALTServerErrorConnectionFailed userInfo:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lockdownd_service_descriptor_free(service);
|
||||||
|
lockdownd_client_free(lockdownClient);
|
||||||
|
idevice_free(device);
|
||||||
|
|
||||||
|
ALTNotificationConnection *notificationConnection = [[ALTNotificationConnection alloc] initWithDevice:altDevice client:client];
|
||||||
|
completionHandler(notificationConnection, nil);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Getters -
|
#pragma mark - Getters -
|
||||||
|
|
||||||
- (NSArray<ALTDevice *> *)connectedDevices
|
- (NSArray<ALTDevice *> *)connectedDevices
|
||||||
@@ -683,3 +782,49 @@ void ALTDeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid)
|
|||||||
NSLog(@"Installation Progress: %@", @(percent));
|
NSLog(@"Installation Progress: %@", @(percent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ALTDeviceDidChangeConnectionStatus(const idevice_event_t *event, void *user_data)
|
||||||
|
{
|
||||||
|
ALTDevice * (^deviceForUDID)(NSString *, NSArray<ALTDevice *> *) = ^ALTDevice *(NSString *udid, NSArray<ALTDevice *> *devices) {
|
||||||
|
for (ALTDevice *device in devices)
|
||||||
|
{
|
||||||
|
if ([device.identifier isEqualToString:udid])
|
||||||
|
{
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (event->event)
|
||||||
|
{
|
||||||
|
case IDEVICE_DEVICE_ADD:
|
||||||
|
{
|
||||||
|
ALTDevice *device = deviceForUDID(@(event->udid), ALTDeviceManager.sharedManager.connectedDevices);
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:ALTDeviceManagerDeviceDidConnectNotification object:device];
|
||||||
|
|
||||||
|
if (device)
|
||||||
|
{
|
||||||
|
[ALTDeviceManager.sharedManager.cachedDevices addObject:device];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IDEVICE_DEVICE_REMOVE:
|
||||||
|
{
|
||||||
|
ALTDevice *device = deviceForUDID(@(event->udid), ALTDeviceManager.sharedManager.cachedDevices.allObjects);
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:ALTDeviceManagerDeviceDidDisconnectNotification object:device];
|
||||||
|
|
||||||
|
if (device)
|
||||||
|
{
|
||||||
|
[ALTDeviceManager.sharedManager.cachedDevices removeObject:device];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -121,6 +121,10 @@
|
|||||||
BF5AB3A92285FE7500DC914B /* AltSign.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF5AB3A92285FE7500DC914B /* AltSign.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
BF5C5FCF237DF69100EDD0C6 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
BF5C5FCF237DF69100EDD0C6 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
||||||
BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6F439123644C6E00A0B879 /* RefreshAltStoreViewController.swift */; };
|
BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6F439123644C6E00A0B879 /* RefreshAltStoreViewController.swift */; };
|
||||||
|
BF718BC923C919E300A89F2D /* CFNotificationName+AltStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */; };
|
||||||
|
BF718BD123C91BD300A89F2D /* ALTWiredConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */; };
|
||||||
|
BF718BD523C928A300A89F2D /* ALTNotificationConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD423C928A300A89F2D /* ALTNotificationConnection.m */; };
|
||||||
|
BF718BD823C93DB700A89F2D /* AltKit.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD723C93DB700A89F2D /* AltKit.m */; };
|
||||||
BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */; };
|
BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */; };
|
||||||
BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */; };
|
BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */; };
|
||||||
BF770E5422BC044E002A40FE /* AppOperationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5322BC044E002A40FE /* AppOperationContext.swift */; };
|
BF770E5422BC044E002A40FE /* AppOperationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E5322BC044E002A40FE /* AppOperationContext.swift */; };
|
||||||
@@ -131,6 +135,7 @@
|
|||||||
BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; };
|
BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; };
|
||||||
BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C322E662D300049BA1 /* AppViewController.swift */; };
|
BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C322E662D300049BA1 /* AppViewController.swift */; };
|
||||||
BF914C262383703800E713BA /* AltPlugin.mailbundle.zip in Resources */ = {isa = PBXBuildFile; fileRef = BF914C252383703800E713BA /* AltPlugin.mailbundle.zip */; };
|
BF914C262383703800E713BA /* AltPlugin.mailbundle.zip in Resources */ = {isa = PBXBuildFile; fileRef = BF914C252383703800E713BA /* AltPlugin.mailbundle.zip */; };
|
||||||
|
BF9A03C623C7DD0D000D08DB /* ClientConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */; };
|
||||||
BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */; };
|
BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */; };
|
||||||
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */; };
|
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */; };
|
||||||
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */; };
|
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */; };
|
||||||
@@ -434,6 +439,15 @@
|
|||||||
BF5C5FCD237DF69100EDD0C6 /* ALTPluginService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTPluginService.h; sourceTree = "<group>"; };
|
BF5C5FCD237DF69100EDD0C6 /* ALTPluginService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTPluginService.h; sourceTree = "<group>"; };
|
||||||
BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTPluginService.m; sourceTree = "<group>"; };
|
BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTPluginService.m; sourceTree = "<group>"; };
|
||||||
BF6F439123644C6E00A0B879 /* RefreshAltStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAltStoreViewController.swift; sourceTree = "<group>"; };
|
BF6F439123644C6E00A0B879 /* RefreshAltStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAltStoreViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CFNotificationName+AltStore.h"; sourceTree = "<group>"; };
|
||||||
|
BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CFNotificationName+AltStore.m"; sourceTree = "<group>"; };
|
||||||
|
BF718BCF23C91BD300A89F2D /* ALTWiredConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTWiredConnection.h; sourceTree = "<group>"; };
|
||||||
|
BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTWiredConnection.m; sourceTree = "<group>"; };
|
||||||
|
BF718BD223C91C7000A89F2D /* ALTWiredConnection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ALTWiredConnection+Private.h"; sourceTree = "<group>"; };
|
||||||
|
BF718BD323C928A300A89F2D /* ALTNotificationConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTNotificationConnection.h; sourceTree = "<group>"; };
|
||||||
|
BF718BD423C928A300A89F2D /* ALTNotificationConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTNotificationConnection.m; sourceTree = "<group>"; };
|
||||||
|
BF718BD623C92B3700A89F2D /* ALTNotificationConnection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ALTNotificationConnection+Private.h"; sourceTree = "<group>"; };
|
||||||
|
BF718BD723C93DB700A89F2D /* AltKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AltKit.m; sourceTree = "<group>"; };
|
||||||
BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardingNavigationController.swift; sourceTree = "<group>"; };
|
BF74989A23621C0700CED65F /* ForwardingNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardingNavigationController.swift; sourceTree = "<group>"; };
|
||||||
BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallAppOperation.swift; sourceTree = "<group>"; };
|
BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallAppOperation.swift; sourceTree = "<group>"; };
|
||||||
BF770E5322BC044E002A40FE /* AppOperationContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppOperationContext.swift; sourceTree = "<group>"; };
|
BF770E5322BC044E002A40FE /* AppOperationContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppOperationContext.swift; sourceTree = "<group>"; };
|
||||||
@@ -444,6 +458,7 @@
|
|||||||
BF8F69C122E659F700049BA1 /* AppContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewController.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>"; };
|
BF8F69C322E662D300049BA1 /* AppViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = "<group>"; };
|
||||||
BF914C252383703800E713BA /* AltPlugin.mailbundle.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = AltPlugin.mailbundle.zip; sourceTree = "<group>"; };
|
BF914C252383703800E713BA /* AltPlugin.mailbundle.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = AltPlugin.mailbundle.zip; 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>"; };
|
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>"; };
|
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>"; };
|
BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
@@ -651,11 +666,14 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFD52BD222A06EFB000B7ED1 /* AltKit.h */,
|
BFD52BD222A06EFB000B7ED1 /* AltKit.h */,
|
||||||
|
BF718BD723C93DB700A89F2D /* AltKit.m */,
|
||||||
BFBAC8852295C90300587369 /* Result+Conveniences.swift */,
|
BFBAC8852295C90300587369 /* Result+Conveniences.swift */,
|
||||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
||||||
BF1E3128229F474900370A3C /* ServerProtocol.swift */,
|
BF1E3128229F474900370A3C /* ServerProtocol.swift */,
|
||||||
BF1E314822A060F400370A3C /* NSError+ALTServerError.h */,
|
BF1E314822A060F400370A3C /* NSError+ALTServerError.h */,
|
||||||
BF1E314922A060F400370A3C /* NSError+ALTServerError.m */,
|
BF1E314922A060F400370A3C /* NSError+ALTServerError.m */,
|
||||||
|
BF718BC723C919CC00A89F2D /* CFNotificationName+AltStore.h */,
|
||||||
|
BF718BC823C919E300A89F2D /* CFNotificationName+AltStore.m */,
|
||||||
);
|
);
|
||||||
path = AltKit;
|
path = AltKit;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1079,6 +1097,13 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF1E3129229F474900370A3C /* ConnectionManager.swift */,
|
BF1E3129229F474900370A3C /* ConnectionManager.swift */,
|
||||||
|
BF9A03C523C7DD0D000D08DB /* ClientConnection.swift */,
|
||||||
|
BF718BCF23C91BD300A89F2D /* ALTWiredConnection.h */,
|
||||||
|
BF718BD223C91C7000A89F2D /* ALTWiredConnection+Private.h */,
|
||||||
|
BF718BD023C91BD300A89F2D /* ALTWiredConnection.m */,
|
||||||
|
BF718BD323C928A300A89F2D /* ALTNotificationConnection.h */,
|
||||||
|
BF718BD623C92B3700A89F2D /* ALTNotificationConnection+Private.h */,
|
||||||
|
BF718BD423C928A300A89F2D /* ALTNotificationConnection.m */,
|
||||||
);
|
);
|
||||||
path = Connections;
|
path = Connections;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1547,9 +1572,11 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
BF718BD823C93DB700A89F2D /* AltKit.m in Sources */,
|
||||||
BF1E315A22A0620000370A3C /* NSError+ALTServerError.m in Sources */,
|
BF1E315A22A0620000370A3C /* NSError+ALTServerError.m in Sources */,
|
||||||
BF1E315922A061FB00370A3C /* Bundle+AltStore.swift in Sources */,
|
BF1E315922A061FB00370A3C /* Bundle+AltStore.swift in Sources */,
|
||||||
BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */,
|
BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */,
|
||||||
|
BF718BC923C919E300A89F2D /* CFNotificationName+AltStore.m in Sources */,
|
||||||
BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */,
|
BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1559,7 +1586,10 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */,
|
BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */,
|
||||||
|
BF718BD523C928A300A89F2D /* ALTNotificationConnection.m in Sources */,
|
||||||
BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */,
|
BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */,
|
||||||
|
BF9A03C623C7DD0D000D08DB /* ClientConnection.swift in Sources */,
|
||||||
|
BF718BD123C91BD300A89F2D /* ALTWiredConnection.m in Sources */,
|
||||||
BF458690229872EA00BD7491 /* AppDelegate.swift in Sources */,
|
BF458690229872EA00BD7491 /* AppDelegate.swift in Sources */,
|
||||||
BF4586C52298CDB800BD7491 /* ALTDeviceManager.mm in Sources */,
|
BF4586C52298CDB800BD7491 /* ALTDeviceManager.mm in Sources */,
|
||||||
BF0241AA22F29CCD00129732 /* UserDefaults+AltServer.swift in Sources */,
|
BF0241AA22F29CCD00129732 /* UserDefaults+AltServer.swift in Sources */,
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "NSError+ALTServerError.h"
|
#import "AltKit.h"
|
||||||
|
|
||||||
#import "ALTAppPermission.h"
|
#import "ALTAppPermission.h"
|
||||||
#import "ALTPatreonBenefitType.h"
|
#import "ALTPatreonBenefitType.h"
|
||||||
#import "ALTSourceUserInfoKey.h"
|
#import "ALTSourceUserInfoKey.h"
|
||||||
|
|||||||
@@ -40,15 +40,23 @@ class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>
|
|||||||
ServerManager.shared.connect(to: server) { (result) in
|
ServerManager.shared.connect(to: server) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): self.finish(.failure(error))
|
case .failure(let error):
|
||||||
|
self.finish(.failure(error))
|
||||||
case .success(let connection):
|
case .success(let connection):
|
||||||
|
print("Sending anisette data request...")
|
||||||
|
|
||||||
let request = AnisetteDataRequest()
|
let request = AnisetteDataRequest()
|
||||||
connection.send(request) { (result) in
|
connection.send(request) { (result) in
|
||||||
|
print("Sent anisette data request!")
|
||||||
|
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): self.finish(.failure(error))
|
case .failure(let error): self.finish(.failure(error))
|
||||||
case .success:
|
case .success:
|
||||||
|
print("Waiting for anisette data...")
|
||||||
connection.receiveResponse() { (result) in
|
connection.receiveResponse() { (result) in
|
||||||
|
print("Receiving anisette data:", result)
|
||||||
|
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): self.finish(.failure(error))
|
case .failure(let error): self.finish(.failure(error))
|
||||||
|
|||||||
@@ -7,13 +7,26 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import AltKit
|
||||||
import Roxas
|
import Roxas
|
||||||
|
|
||||||
|
private extension Notification.Name
|
||||||
|
{
|
||||||
|
static let didReceiveWiredServerConnectionResponse = Notification.Name("io.altstore.didReceiveWiredServerConnectionResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
private let ReceivedWiredServerConnectionResponse: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =
|
||||||
|
{ (center, observer, name, object, userInfo) in
|
||||||
|
NotificationCenter.default.post(name: .didReceiveWiredServerConnectionResponse, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
@objc(FindServerOperation)
|
@objc(FindServerOperation)
|
||||||
class FindServerOperation: ResultOperation<Server>
|
class FindServerOperation: ResultOperation<Server>
|
||||||
{
|
{
|
||||||
let group: OperationGroup
|
let group: OperationGroup
|
||||||
|
|
||||||
|
private var isWiredServerConnectionAvailable = false
|
||||||
|
|
||||||
init(group: OperationGroup)
|
init(group: OperationGroup)
|
||||||
{
|
{
|
||||||
self.group = group
|
self.group = group
|
||||||
@@ -31,21 +44,49 @@ class FindServerOperation: ResultOperation<Server>
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let server = ServerManager.shared.discoveredServers.first(where: { $0.isPreferred })
|
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||||
{
|
|
||||||
// Preferred server.
|
// Prepare observers to receive callback from wired server (if connected).
|
||||||
self.finish(.success(server))
|
CFNotificationCenterAddObserver(notificationCenter, nil, ReceivedWiredServerConnectionResponse, CFNotificationName.wiredServerConnectionAvailableResponse.rawValue, nil, .deliverImmediately)
|
||||||
}
|
NotificationCenter.default.addObserver(self, selector: #selector(FindServerOperation.didReceiveWiredServerConnectionResponse(_:)), name: .didReceiveWiredServerConnectionResponse, object: nil)
|
||||||
else if let server = ServerManager.shared.discoveredServers.first
|
|
||||||
{
|
// Post notification.
|
||||||
// Any available server.
|
CFNotificationCenterPostNotification(notificationCenter, .wiredServerConnectionAvailableRequest, nil, nil, true)
|
||||||
self.finish(.success(server))
|
|
||||||
}
|
// Wait for either callback or timeout.
|
||||||
else
|
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
|
||||||
{
|
if self.isWiredServerConnectionAvailable
|
||||||
// No servers.
|
{
|
||||||
self.finish(.failure(ConnectionError.serverNotFound))
|
let server = Server(isWiredConnection: true)
|
||||||
|
self.finish(.success(server))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if let server = ServerManager.shared.discoveredServers.first(where: { $0.isPreferred })
|
||||||
|
{
|
||||||
|
// Preferred server.
|
||||||
|
self.finish(.success(server))
|
||||||
|
}
|
||||||
|
else if let server = ServerManager.shared.discoveredServers.first
|
||||||
|
{
|
||||||
|
// Any available server.
|
||||||
|
self.finish(.success(server))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No servers.
|
||||||
|
self.finish(.failure(ConnectionError.serverNotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension FindServerOperation
|
||||||
|
{
|
||||||
|
@objc func didReceiveWiredServerConnectionResponse(_ notification: Notification)
|
||||||
|
{
|
||||||
|
self.isWiredServerConnectionAvailable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,17 +44,22 @@ enum ConnectionError: LocalizedError
|
|||||||
|
|
||||||
struct Server: Equatable
|
struct Server: Equatable
|
||||||
{
|
{
|
||||||
var identifier: String
|
var identifier: String? = nil
|
||||||
var service: NetService
|
var service: NetService? = nil
|
||||||
|
|
||||||
var isPreferred = false
|
var isPreferred = false
|
||||||
|
var isWiredConnection = false
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Server
|
||||||
|
{
|
||||||
|
// Defined in extension so we can still use the automatically synthesized initializer.
|
||||||
init?(service: NetService, txtData: Data)
|
init?(service: NetService, txtData: Data)
|
||||||
{
|
{
|
||||||
let txtDictionary = NetService.dictionary(fromTXTRecord: txtData)
|
let txtDictionary = NetService.dictionary(fromTXTRecord: txtData)
|
||||||
guard let identifierData = txtDictionary["serverID"], let identifier = String(data: identifierData, encoding: .utf8) else { return nil }
|
guard let identifierData = txtDictionary["serverID"], let identifier = String(data: identifierData, encoding: .utf8) else { return nil }
|
||||||
|
|
||||||
self.identifier = identifier
|
|
||||||
self.service = service
|
self.service = service
|
||||||
|
self.identifier = identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ class ServerManager: NSObject
|
|||||||
private(set) var discoveredServers = [Server]()
|
private(set) var discoveredServers = [Server]()
|
||||||
|
|
||||||
private let serviceBrowser = NetServiceBrowser()
|
private let serviceBrowser = NetServiceBrowser()
|
||||||
|
|
||||||
private var services = Set<NetService>()
|
private var services = Set<NetService>()
|
||||||
|
|
||||||
private let dispatchQueue = DispatchQueue(label: "io.altstore.ServerManager")
|
private let dispatchQueue = DispatchQueue(label: "io.altstore.ServerManager")
|
||||||
|
|
||||||
|
private lazy var connectionListener = self.makeListener()
|
||||||
|
private var incomingConnections = [NWConnection]()
|
||||||
|
private let incomingConnectionsSemaphore = DispatchSemaphore(value: 0)
|
||||||
|
|
||||||
private override init()
|
private override init()
|
||||||
{
|
{
|
||||||
super.init()
|
super.init()
|
||||||
@@ -41,6 +44,8 @@ extension ServerManager
|
|||||||
self.isDiscovering = true
|
self.isDiscovering = true
|
||||||
|
|
||||||
self.serviceBrowser.searchForServices(ofType: ALTServerServiceType, inDomain: "")
|
self.serviceBrowser.searchForServices(ofType: ALTServerServiceType, inDomain: "")
|
||||||
|
|
||||||
|
self.connectionListener.start(queue: self.dispatchQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopDiscovering()
|
func stopDiscovering()
|
||||||
@@ -55,30 +60,62 @@ extension ServerManager
|
|||||||
|
|
||||||
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
|
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
|
||||||
{
|
{
|
||||||
let connection = NWConnection(to: .service(name: server.service.name, type: server.service.type, domain: server.service.domain, interface: nil), using: .tcp)
|
DispatchQueue.global().async {
|
||||||
|
func finish(_ result: Result<ServerConnection, Error>)
|
||||||
connection.stateUpdateHandler = { [unowned connection] (state) in
|
|
||||||
switch state
|
|
||||||
{
|
{
|
||||||
case .failed(let error):
|
completion(result)
|
||||||
print("Failed to connect to service \(server.service.name).", error)
|
}
|
||||||
completion(.failure(ConnectionError.connectionFailed))
|
|
||||||
|
func start(_ connection: NWConnection)
|
||||||
|
{
|
||||||
|
connection.stateUpdateHandler = { [unowned connection] (state) in
|
||||||
|
switch state
|
||||||
|
{
|
||||||
|
case .failed(let error):
|
||||||
|
print("Failed to connect to service \(server.service?.name ?? "").", error)
|
||||||
|
finish(.failure(ConnectionError.connectionFailed))
|
||||||
|
|
||||||
|
case .cancelled:
|
||||||
|
finish(.failure(OperationError.cancelled))
|
||||||
|
|
||||||
|
case .ready:
|
||||||
|
let connection = ServerConnection(server: server, connection: connection)
|
||||||
|
finish(.success(connection))
|
||||||
|
|
||||||
|
case .waiting: break
|
||||||
|
case .setup: break
|
||||||
|
case .preparing: break
|
||||||
|
@unknown default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case .cancelled:
|
connection.start(queue: self.dispatchQueue)
|
||||||
completion(.failure(OperationError.cancelled))
|
}
|
||||||
|
|
||||||
|
if server.isWiredConnection
|
||||||
|
{
|
||||||
|
print("Waiting for new wired connection...")
|
||||||
|
|
||||||
case .ready:
|
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||||
let connection = ServerConnection(server: server, connection: connection)
|
CFNotificationCenterPostNotification(notificationCenter, .wiredServerConnectionStartRequest, nil, nil, true)
|
||||||
completion(.success(connection))
|
|
||||||
|
|
||||||
case .waiting: break
|
_ = self.incomingConnectionsSemaphore.wait(timeout: .now() + 10.0)
|
||||||
case .setup: break
|
|
||||||
case .preparing: break
|
if let connection = self.incomingConnections.popLast()
|
||||||
@unknown default: break
|
{
|
||||||
|
start(connection)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finish(.failure(ALTServerError(.connectionFailed)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if let service = server.service
|
||||||
|
{
|
||||||
|
let connection = NWConnection(to: .service(name: service.name, type: service.type, domain: service.domain, interface: nil), using: .tcp)
|
||||||
|
start(connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.start(queue: self.dispatchQueue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +130,27 @@ private extension ServerManager
|
|||||||
|
|
||||||
self.discoveredServers.append(server)
|
self.discoveredServers.append(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeListener() -> NWListener
|
||||||
|
{
|
||||||
|
let listener = try! NWListener(using: .tcp, on: NWEndpoint.Port(rawValue: ALTDeviceListeningSocket)!)
|
||||||
|
listener.newConnectionHandler = { [weak self] (connection) in
|
||||||
|
self?.incomingConnections.append(connection)
|
||||||
|
self?.incomingConnectionsSemaphore.signal()
|
||||||
|
}
|
||||||
|
listener.stateUpdateHandler = { (state) in
|
||||||
|
switch state
|
||||||
|
{
|
||||||
|
case .ready: break
|
||||||
|
case .waiting, .setup: print("Listener socket waiting...")
|
||||||
|
case .cancelled: print("Listener socket cancelled.")
|
||||||
|
case .failed(let error): print("Listener socket failed:", error)
|
||||||
|
@unknown default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ServerManager: NetServiceBrowserDelegate
|
extension ServerManager: NetServiceBrowserDelegate
|
||||||
|
|||||||
2
Dependencies/AltSign
vendored
2
Dependencies/AltSign
vendored
Submodule Dependencies/AltSign updated: 85d1aa1bd3...5f3d85cf24
Reference in New Issue
Block a user