Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added InterAppCommunication via /dev/iac #315

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#import <UIKit/UIKit.h>

#define kGroupName @"group.app.ish.iSH"

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
Expand Down
19 changes: 18 additions & 1 deletion app/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Theodore Dubois on 10/17/17.
//


#include <resolv.h>
#include <arpa/inet.h>
#include <netdb.h>
Expand All @@ -14,6 +15,9 @@
#include "kernel/init.h"
#include "kernel/calls.h"

#import "IOSGateway.h"


@interface AppDelegate ()

@property BOOL exiting;
Expand All @@ -36,7 +40,7 @@ @implementation AppDelegate

- (int)startThings {
NSFileManager *manager = [NSFileManager defaultManager];
NSURL *container = [manager containerURLForSecurityApplicationGroupIdentifier:@"group.app.ish.iSH"];
NSURL *container = [manager containerURLForSecurityApplicationGroupIdentifier:kGroupName];
NSURL *alpineRoot = [container URLByAppendingPathComponent:@"roots/alpine"];
[manager createDirectoryAtURL:[container URLByAppendingPathComponent:@"roots"]
withIntermediateDirectories:YES
Expand Down Expand Up @@ -124,6 +128,9 @@ - (int)startThings {
generic_mknod("/dev/random", S_IFCHR|0666, dev_make(1, 8));
generic_mknod("/dev/urandom", S_IFCHR|0666, dev_make(1, 9));

generic_mknod("/dev/iac", S_IFCHR|0666, dev_make(1, 99));


do_mount(&procfs, "proc", "/proc");
do_mount(&devptsfs, "devpts", "/dev/pts");

Expand All @@ -135,6 +142,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// get the network permissions popup to appear on chinese devices
[[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:@"http://captive.apple.com"]] resume];

[[IOSGateway sharedSession] setup];

[UserPreferences.shared addObserver:self forKeyPath:@"shouldDisableDimming" options:NSKeyValueObservingOptionInitial context:nil];
int err = [self startThings];
if (err < 0) {
Expand All @@ -148,6 +157,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
return YES;
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
if ([[IOSGateway sharedSession] canHandleOpeningURL:url]) {
return YES;
}
return NO;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
UIApplication.sharedApplication.idleTimerDisabled = UserPreferences.shared.shouldDisableDimming;
}
Expand Down
24 changes: 24 additions & 0 deletions app/IOSGateway.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// IOSGateway.h
// iSH
//
// Created by Miguel Vanhove on 15/02/2019.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface IOSGateway : NSObject

+ (IOSGateway *)sharedSession;

- (void)setup;
- (BOOL)canHandleOpeningURL:(NSURL *)url;

@end

extern size_t iac_read(void *buf, size_t bufsize);
extern size_t iac_write(const void *buf, size_t bufsize);

NS_ASSUME_NONNULL_END
108 changes: 108 additions & 0 deletions app/IOSGateway.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// IOSGateway.m
// iSH
//
// Created by Miguel Vanhove on 15/02/2019.
//

#import "IOSGateway.h"
#import "IACManager.h"
#import "UIApplication+OpenURL.h"

static IOSGateway *iOSGateway = nil;

@interface IOSGateway ()
redbug26 marked this conversation as resolved.
Show resolved Hide resolved

@property (copy, nonatomic) NSData *iacResult;

@end

@implementation IOSGateway

+ (IOSGateway *)sharedSession
{
if (iOSGateway == nil) {
iOSGateway = [IOSGateway new];
}

return iOSGateway;
}

- (id)init
{
self = [super init];
if (self != nil) {
redbug26 marked this conversation as resolved.
Show resolved Hide resolved
self.iacResult = [[NSData alloc] init];
}
return self;
}

- (void)setup
{
#ifndef TARGET_IS_EXTENSION

[IACManager sharedManager].callbackURLScheme = @"x-ish";

[[IACManager sharedManager] handleAction:@"iac"
withBlock: ^(NSDictionary *inputParameters, IACSuccessBlock success, IACFailureBlock failure) {
if (success) {
NSError *__autoreleasing jserr = nil;

self.iacResult = [NSJSONSerialization dataWithJSONObject:inputParameters options:0 error:&jserr];

success(@{ @"names": @"json" }, NO);
}
}];
#endif
}

- (BOOL)canHandleOpeningURL:(NSURL *)url
{
#ifndef TARGET_IS_EXTENSION
return [[IACManager sharedManager] handleOpenURL:url];
#else
return false;
#endif
}

extern size_t iac_write(const void *buf, size_t bufsize)
{
#ifndef TARGET_IS_EXTENSION

NSData *data = [NSData dataWithBytes:buf length:bufsize];
NSString *command = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

dispatch_sync(dispatch_get_main_queue(), ^{
IOSGateway *ic = [IOSGateway sharedSession];

NSString *cmdString = [command stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

ic.iacResult = [[NSData alloc] init]; // Reset result

[UIApplication openURL:cmdString];
});

#endif

return bufsize;
}

extern size_t iac_read(void *buf, size_t bufsize)
redbug26 marked this conversation as resolved.
Show resolved Hide resolved
{
#ifndef TARGET_IS_EXTENSION

IOSGateway *ic = [IOSGateway sharedSession];

NSUInteger length = (bufsize < [ic.iacResult length]) ? bufsize : [ic.iacResult length];
memcpy(buf, [ic.iacResult bytes], length);

ic.iacResult = [ic.iacResult subdataWithRange:NSMakeRange(bufsize, [ic.iacResult length] - length)];

return length;

#endif

return 0;
}

@end
15 changes: 13 additions & 2 deletions app/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,27 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>x-ish</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>48</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
Expand Down
50 changes: 50 additions & 0 deletions app/InterAppCommunication/IACClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// IACClient.h
// IACSample
//
// Created by Antonio Cabezuelo Vivo on 09/02/13.
// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "IACManager.h"

/* This is the class used to make calls to external apps. Use this class as a superclass to create classes for your own apps. Thjis way you can offer a clean API to your app that can meke interact with it easier for other apps.
*/
@interface IACClient : NSObject

// URL scheme that the external app is listenig to. This is mandatory.
@property (copy, nonatomic) NSString *URLScheme;

// The manager to use for calls from this client. If not set, IACManager shared instance will be used.
@property (weak, nonatomic) IACManager *manager;

// Initializers
+ (instancetype)client;
+ (instancetype)clientWithURLScheme:(NSString*)scheme;
- (instancetype)initWithURLScheme:(NSString*)scheme;

/* Utility method to test if the app that responds to the URLScheme is installed in the device.
*/
- (BOOL)isAppInstalled;


/* Method that transforms from x-callback-url errorCode parameter to a NSInteger to be used in NSError's code.
The default implementation return [code integerValue].
If you create a subclass for your app and your app return string error codes you must implement this method to transform from your error codes to integer values.
*/
- (NSInteger)NSErrorCodeForXCUErrorCode:(NSString*)code;

/* Convenient methods to make call to external apps. If you create a subclass for your app, call these methods to launch the external app.
*/
- (void)performAction:(NSString*)action;

- (void)performAction:(NSString*)action
parameters:(NSDictionary*)params;

- (void)performAction:(NSString*)action
parameters:(NSDictionary*)params
onSuccess:(void(^)(NSDictionary*result))success
redbug26 marked this conversation as resolved.
Show resolved Hide resolved
onFailure:(void(^)(NSError*))failure;

@end
72 changes: 72 additions & 0 deletions app/InterAppCommunication/IACClient.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// IACClient.m
// IACSample
//
// Created by Antonio Cabezuelo Vivo on 09/02/13.
// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved.
//

#import <UIKit/UIKit.h>

#import "IACClient.h"
#import "IACRequest.h"
#import "IACManager.h"

#if !__has_feature(objc_arc)
#error InterAppComutication must be built with ARC.
// You can turn on ARC for only InterAppComutication files by adding -fobjc-arc to the build phase for each of its files.
#endif


@implementation IACClient

+ (instancetype)client {
return [[self alloc] init];
}

+ (instancetype)clientWithURLScheme:(NSString*)scheme {
return [[self alloc] initWithURLScheme:scheme];
}

- (instancetype)initWithURLScheme:(NSString*)scheme {
self = [super init];
if (self) {
self.URLScheme = scheme;
}
return self;
}

- (NSInteger)NSErrorCodeForXCUErrorCode:(NSString*)code {
return [code integerValue];
}

- (BOOL)isAppInstalled {
return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://Test", self.URLScheme]]];
}

- (void)performAction:(NSString*)action {
[self performAction:action parameters:nil];
}

- (void)performAction:(NSString*)action parameters:(NSDictionary*)params {
[self performAction:action parameters:params onSuccess:nil onFailure:nil];
}


- (void)performAction:(NSString*)action parameters:(NSDictionary*)params onSuccess:(void(^)(NSDictionary*result))success onFailure:(void(^)(NSError*))failure {

IACRequest *request = [[IACRequest alloc] init];
request.client = self;
request.action = action;
request.parameters = params;
request.successCalback = success;
request.errorCalback = failure;

if (self.manager) {
[self.manager sendIACRequest:request];
} else {
[[IACManager sharedManager] sendIACRequest:request];
}
}

@end
32 changes: 32 additions & 0 deletions app/InterAppCommunication/IACDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// IACDelegate.h
// IACSample
//
// Created by Antonio Cabezuelo Vivo on 11/02/13.
// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved.
//

#import <Foundation/Foundation.h>


// Block templates
typedef void(^IACSuccessBlock)(NSDictionary* returnParams,BOOL cancelled);
typedef void(^IACFailureBlock)(NSError* error);


@protocol IACDelegate <NSObject>

/* Method invoqued to see if an action is handled by the delegate
*/
- (BOOL)supportsIACAction:(NSString*)action;

/* Method invoqued by the manager to perform an action.
The parameters dictionary does not contain any x-callback-url parameter except 'x-source'.
success and failure are the blocks you must call after you perform the action to support callbacks to the calling app. If the action does not support callbacks you can ignore this blocks.
*/
- (void)performIACAction:(NSString*)action
parameters:(NSDictionary*)parameters
onSuccess:(IACSuccessBlock)success
onFailure:(IACFailureBlock)failure;

@end
Loading