diff --git a/app/AppDelegate.h b/app/AppDelegate.h index a3c8b3a66b..2db41995a2 100644 --- a/app/AppDelegate.h +++ b/app/AppDelegate.h @@ -7,6 +7,8 @@ #import +#define kGroupName @"group.app.ish.iSH" + @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; diff --git a/app/AppDelegate.m b/app/AppDelegate.m index 24ebf15de3..a347833c36 100644 --- a/app/AppDelegate.m +++ b/app/AppDelegate.m @@ -5,6 +5,7 @@ // Created by Theodore Dubois on 10/17/17. // + #include #include #include @@ -14,6 +15,9 @@ #include "kernel/init.h" #include "kernel/calls.h" +#import "IOSGateway.h" + + @interface AppDelegate () @property BOOL exiting; @@ -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 @@ -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"); @@ -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) { @@ -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 *)change context:(void *)context { UIApplication.sharedApplication.idleTimerDisabled = UserPreferences.shared.shouldDisableDimming; } diff --git a/app/IOSGateway.h b/app/IOSGateway.h new file mode 100644 index 0000000000..d1d762710c --- /dev/null +++ b/app/IOSGateway.h @@ -0,0 +1,24 @@ +// +// IOSGateway.h +// iSH +// +// Created by Miguel Vanhove on 15/02/2019. +// + +#import + +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 diff --git a/app/IOSGateway.m b/app/IOSGateway.m new file mode 100644 index 0000000000..99f9cf1d0b --- /dev/null +++ b/app/IOSGateway.m @@ -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 () + +@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) { + 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) +{ +#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 diff --git a/app/Info.plist b/app/Info.plist index 356ec49fba..9dba372cfc 100644 --- a/app/Info.plist +++ b/app/Info.plist @@ -16,16 +16,27 @@ APPL CFBundleShortVersionString 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + x-ish + + + CFBundleVersion 48 LSRequiresIPhoneOS + UIFileSharingEnabled + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main - UIFileSharingEnabled - UIRequiredDeviceCapabilities armv7 diff --git a/app/InterAppCommunication/IACClient.h b/app/InterAppCommunication/IACClient.h new file mode 100644 index 0000000000..c92778ef5b --- /dev/null +++ b/app/InterAppCommunication/IACClient.h @@ -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 +#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 + onFailure:(void(^)(NSError*))failure; + +@end diff --git a/app/InterAppCommunication/IACClient.m b/app/InterAppCommunication/IACClient.m new file mode 100644 index 0000000000..259a9f11c5 --- /dev/null +++ b/app/InterAppCommunication/IACClient.m @@ -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 + +#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 diff --git a/app/InterAppCommunication/IACDelegate.h b/app/InterAppCommunication/IACDelegate.h new file mode 100644 index 0000000000..7c777a2a27 --- /dev/null +++ b/app/InterAppCommunication/IACDelegate.h @@ -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 + + +// Block templates +typedef void(^IACSuccessBlock)(NSDictionary* returnParams,BOOL cancelled); +typedef void(^IACFailureBlock)(NSError* error); + + +@protocol IACDelegate + +/* 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 diff --git a/app/InterAppCommunication/IACManager.h b/app/InterAppCommunication/IACManager.h new file mode 100644 index 0000000000..c8d5aec21c --- /dev/null +++ b/app/InterAppCommunication/IACManager.h @@ -0,0 +1,56 @@ +// +// IACManager.h +// IACSample +// +// Created by Antonio Cabezuelo Vivo on 09/02/13. +// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved. +// + +#import +#import + +#import "IACDelegate.h" + +@protocol IACDelegate; +@class IACRequest; + +// Error domains that this framework will use in error callbacks +extern NSString * const IACErrorDomain; +extern NSString * const IACClientErrorDomain; + +// Predefined error codes +typedef NS_ENUM(NSInteger, IACError) { + IACErrorAppNotInstalled = 1, + IACErrorNotSupportedAction = 2 +}; + +// Block template for action handlers +typedef void(^IACActionHandlerBlock)(NSDictionary* inputParameters, IACSuccessBlock success, IACFailureBlock failure); + + +@interface IACManager : NSObject + +// Delegate to be called when an x-callback-url API call is made for this app +@property (weak, nonatomic) id delegate; + +// The URL scheme the app is listening on. It must be defined in Info.plist. If your app is not listening or do not expect callbacks you can leave this empty +@property (copy, nonatomic) NSString *callbackURLScheme; + + ++ (IACManager*)sharedManager; + +/* Method to use in app delegate url handler methods. + Handles the URL parsing and invocation of the different handlers and delegate methods. + The IACManager should be initialized with the URL scheme that you want to respond to before make any call that expect callbacks. +*/ +- (BOOL)handleOpenURL:(NSURL*)url; + +/* Method to add action handlers for your x-callback-url APIs +*/ +- (void)handleAction:(NSString*)action withBlock:(IACActionHandlerBlock)handler; + +/* Method to send request to external apps +*/ +- (void)sendIACRequest:(IACRequest*)request; + +@end diff --git a/app/InterAppCommunication/IACManager.m b/app/InterAppCommunication/IACManager.m new file mode 100644 index 0000000000..2de3ce5a20 --- /dev/null +++ b/app/InterAppCommunication/IACManager.m @@ -0,0 +1,260 @@ +// +// IACManager.m +// IACSample +// +// Created by Antonio Cabezuelo Vivo on 09/02/13. +// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved. +// + +#import "IACManager.h" +#import "IACDelegate.h" +#import "IACClient.h" +#import "IACRequest.h" +#import "NSString+IACExtensions.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 + + +NSString * const IACErrorDomain = @"com.iac.manager.error"; +NSString * const IACClientErrorDomain = @"com.iac.client.error"; + +// x-callback-url strings +static NSString * const kXCUPrefix = @"x-"; +static NSString * const kXCUHost = @"x-callback-url"; +static NSString * const kXCUSource = @"x-source"; +static NSString * const kXCUSuccess = @"x-success"; +static NSString * const kXCUError = @"x-error"; +static NSString * const kXCUCancel = @"x-cancel"; +static NSString * const kXCUErrorCode = @"error-Code"; +static NSString * const kXCUErrorMessage = @"errorMessage"; + +// IAC strings +static NSString * const kIACPrefix = @"IAC"; +static NSString * const kIACResponse = @"IACRequestResponse"; +static NSString * const kIACRequest = @"IACRequestID"; +static NSString * const kIACResponseType = @"IACResponseType"; +static NSString * const kIACErrorDomain = @"errorDomain"; + +typedef NS_ENUM(NSUInteger, IACResponseType) { + IACResponseTypeSuccess, + IACResponseTypeFailure, + IACResponseTypeCancel +}; + + +@implementation IACManager { + NSMutableDictionary *sessions; + NSMutableDictionary *actions; +} + ++ (IACManager*)sharedManager { + static IACManager *sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedManager = [[self alloc] init]; + }); + + return sharedManager; +} + +- (instancetype)init { + self = [super init]; + if (self) { + sessions = [NSMutableDictionary dictionary]; + actions = [NSMutableDictionary dictionary]; + } + return self; +} + +- (BOOL)handleOpenURL:(NSURL*)url { + // An app can respond to multiple url schemes and the app can use different IACManagers for each one + // so we test if the url is handled by this manager + if (![url.scheme isEqualToString:self.callbackURLScheme]) { + return NO; + } + + // If the url is an x-callback-url compatible url we handle it + if ([url.host isEqualToString:kXCUHost]) { + NSString *action = [[url path] substringFromIndex:1]; + NSDictionary *parameters = [url.query parseURLParams]; + NSDictionary *actionParamters = [self removeProtocolParamsFromDictionary:parameters]; + + + // Lets see if this is a response to a previous call + if ([action isEqualToString:kIACResponse]) { + NSString *requestID = parameters[kIACRequest]; + + IACRequest *request = sessions[requestID]; + if (request) { + IACResponseType responseType = [parameters[kIACResponseType] intValue]; + + switch (responseType) { + case IACResponseTypeSuccess: + if (request.successCalback) { + request.successCalback(actionParamters); + } + break; + + case IACResponseTypeFailure: + if (request.errorCalback) { + NSInteger errorCode = [request.client NSErrorCodeForXCUErrorCode:parameters[kXCUErrorCode]]; + NSString *errorDomain = parameters[kIACErrorDomain] ? parameters[kIACErrorDomain] : IACClientErrorDomain; + NSError *error = [NSError errorWithDomain:errorDomain + code:errorCode + userInfo:@{NSLocalizedDescriptionKey: parameters[kXCUErrorMessage]}]; + + request.errorCalback(error); + } + break; + + case IACResponseTypeCancel: + if (request.successCalback) { + request.successCalback(nil); + } + break; + + default: + [sessions removeObjectForKey:requestID]; + return NO; + break; + } + + [sessions removeObjectForKey:requestID]; + return YES; + } + + return NO; + } + + // Lets see if there is somebody that handles this action + if (actions[action] || [self.delegate supportsIACAction:action]) { + + IACSuccessBlock success = ^(NSDictionary *returnParams, BOOL cancelled) { + if (cancelled) { + if (parameters[kXCUCancel]) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:parameters[kXCUCancel]]]; + } + } else if (parameters[kXCUSuccess]) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[parameters[kXCUSuccess] stringByAppendingURLParams:returnParams]]]; + } + }; + + IACFailureBlock failure = ^(NSError *error) { + if (parameters[kXCUError]) { + NSDictionary *errorParams = @{ kXCUErrorCode: @([error code]), + kXCUErrorMessage: [error localizedDescription], + kIACErrorDomain: [error domain] + }; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[parameters[kXCUError] stringByAppendingURLParams:errorParams]]]; + } + }; + + // Handlers take precedence over the delegate + if (actions[action]) { + IACActionHandlerBlock actionHandler = actions[action]; + actionHandler(actionParamters, success, failure); + return YES; + + } else if ([self.delegate supportsIACAction:action]) { + [self.delegate performIACAction:action + parameters:actionParamters + onSuccess:success + onFailure:failure]; + + return YES; + } + } else { + if (parameters[kXCUError]) { + NSDictionary *errorParams = @{ kXCUErrorCode: @(IACErrorNotSupportedAction), + kXCUErrorMessage: [NSString stringWithFormat:NSLocalizedString(@"'%@' is not an x-callback-url action supported by %@", nil), action, [self localizedAppName]], + kIACErrorDomain: IACErrorDomain + }; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[parameters[kXCUError] stringByAppendingURLParams:errorParams]]]; + return YES; + } + } + } + + + return NO; +} + +- (void)sendIACRequest:(IACRequest*)request { + + if (![request.client isAppInstalled]) { + if (request.errorCalback) { + NSError *error = [NSError errorWithDomain:IACErrorDomain + code:IACErrorAppNotInstalled + userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:NSLocalizedString(@"App with scheme '%@' is not installed in this device", nil), request.client.URLScheme]}]; + request.errorCalback(error); + } + return; + } + + NSString *final_url = [NSString stringWithFormat:@"%@://%@/%@?", request.client.URLScheme, kXCUHost, request.action]; + final_url = [final_url stringByAppendingURLParams:request.parameters]; + final_url = [final_url stringByAppendingURLParams:@{kXCUSource: [self localizedAppName]}]; + + if (self.callbackURLScheme) { + NSString *xcu = [NSString stringWithFormat:@"%@://%@/%@?", self.callbackURLScheme, kXCUHost, kIACResponse]; + xcu = [xcu stringByAppendingURLParams:@{kIACRequest:request.requestID}]; + + NSMutableDictionary *xcu_params = [NSMutableDictionary dictionary]; + + if (request.successCalback) { + xcu_params[kXCUSuccess] = [xcu stringByAppendingURLParams:@{kIACResponseType:@(IACResponseTypeSuccess)}]; + xcu_params[kXCUCancel] = [xcu stringByAppendingURLParams:@{kIACResponseType:@(IACResponseTypeCancel)}]; + } + + if (request.errorCalback) { + xcu_params[kXCUError] = [xcu stringByAppendingURLParams:@{kIACResponseType:@(IACResponseTypeFailure)}]; + } + + final_url = [final_url stringByAppendingURLParams:xcu_params]; + } else if (request.successCalback || request.errorCalback) { + NSLog(@"WARNING: If you want to support callbacks from the remote app you must define a URL Scheme for this app to listen on"); + } + + sessions[request.requestID] = request; + + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:final_url]]; +} + + +- (void)handleAction:(NSString*)action withBlock:(IACActionHandlerBlock)handler { + actions[action] = [handler copy]; +} + + +- (NSDictionary*)removeProtocolParamsFromDictionary:(NSDictionary*)dictionary { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + // Removes all x-callback-url and all IAC parameters + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if (![key hasPrefix:kXCUPrefix] && ![key hasPrefix:kIACPrefix]) { + [result setObject:obj forKey:key]; + } + }]; + + // Adds x-source parameter as this is needed to inform the user + if (dictionary[kXCUSource]) { + result[kXCUSource] = dictionary[kXCUSource]; + } + + return result; +} + +- (NSString*)localizedAppName { + NSString *appname = [[NSBundle mainBundle] localizedInfoDictionary][@"CFBundleDisplayName"]; + if (!appname) { + appname = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; + } + + return appname; +} + +@end diff --git a/app/InterAppCommunication/IACRequest.h b/app/InterAppCommunication/IACRequest.h new file mode 100644 index 0000000000..bcb25e4407 --- /dev/null +++ b/app/InterAppCommunication/IACRequest.h @@ -0,0 +1,22 @@ +// +// IACRequest.h +// IACSample +// +// Created by Antonio Cabezuelo Vivo on 09/02/13. +// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved. +// + +#import + +@class IACClient; + +@interface IACRequest : NSObject + +@property (copy, readonly, nonatomic) NSString *requestID; +@property (strong, nonatomic) IACClient *client; +@property (copy, nonatomic) NSString *action; +@property (strong, nonatomic) NSDictionary *parameters; +@property (copy, nonatomic) void(^successCalback)(NSDictionary*); +@property (copy, nonatomic) void(^errorCalback)(NSError*); + +@end diff --git a/app/InterAppCommunication/IACRequest.m b/app/InterAppCommunication/IACRequest.m new file mode 100644 index 0000000000..349be22749 --- /dev/null +++ b/app/InterAppCommunication/IACRequest.m @@ -0,0 +1,33 @@ +// +// IACRequest.m +// IACSample +// +// Created by Antonio Cabezuelo Vivo on 09/02/13. +// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved. +// + +#import "IACRequest.h" +#import "NSString+IACExtensions.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 + + +@interface IACRequest () +@property (copy, readwrite, nonatomic) NSString *requestID; +@end + +@implementation IACRequest + +- (instancetype)init { + self = [super init]; + if (self) { + self.requestID = [NSString stringWithUUID]; + } + return self; +} + + +@end diff --git a/app/InterAppCommunication/NSString+IACExtensions.h b/app/InterAppCommunication/NSString+IACExtensions.h new file mode 100644 index 0000000000..2847eee500 --- /dev/null +++ b/app/InterAppCommunication/NSString+IACExtensions.h @@ -0,0 +1,19 @@ +// +// NSString+IACExtensions.h +// IACSample +// +// Created by Antonio Cabezuelo Vivo on 10/02/13. +// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved. +// + +#import + +@interface NSString (IACExtensions) + ++ (NSString*)stringWithUUID; + +- (NSString*)stringByAppendingURLParams:(NSDictionary*)params; + +- (NSDictionary*)parseURLParams; + +@end diff --git a/app/InterAppCommunication/NSString+IACExtensions.m b/app/InterAppCommunication/NSString+IACExtensions.m new file mode 100644 index 0000000000..2182d778d2 --- /dev/null +++ b/app/InterAppCommunication/NSString+IACExtensions.m @@ -0,0 +1,71 @@ +// +// NSString+IACExtensions.m +// IACSample +// +// Created by Antonio Cabezuelo Vivo on 10/02/13. +// Copyright (c) 2013 Antonio Cabezuelo Vivo. All rights reserved. +// + +#import "NSString+IACExtensions.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 NSString (IACExtensions) + ++ (NSString*)stringWithUUID { + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + NSString *uuidStr = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid); + CFRelease(uuid); + + return uuidStr; +} + + +- (NSDictionary*)parseURLParams { + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; + + NSArray *pairs = [self componentsSeparatedByString:@"&"]; + + [pairs enumerateObjectsUsingBlock:^(NSString *pair, NSUInteger idx, BOOL *stop) { + NSArray *comps = [pair componentsSeparatedByString:@"="]; + if ([comps count] == 2) { + [result setObject:[comps[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:comps[0]]; + } + }]; + + return result; +} + +- (NSString*)stringByAppendingURLParams:(NSDictionary*)params { + NSMutableString *result = [[NSMutableString alloc] init]; + + [result appendString:self]; + + if ([result rangeOfString:@"?"].location != NSNotFound) { + if (![result hasSuffix:@"&"]) + [result appendString:@"&"]; + } else { + [result appendString:@"?"]; + } + + [params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + NSString *escapedObj = obj; + if ([obj isKindOfClass:[NSString class]]) { + escapedObj = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes( + NULL, + (__bridge CFStringRef) obj, + NULL, + CFSTR("!*'();:@&=+$,/?%#[]"), + kCFStringEncodingUTF8)); + } + [result appendFormat:@"%@=%@&", key, escapedObj]; + }]; + + return result; +} + +@end diff --git a/fs/mem.c b/fs/mem.c index f0e7e0f385..27e928464f 100644 --- a/fs/mem.c +++ b/fs/mem.c @@ -4,6 +4,9 @@ #include "fs/mem.h" #include "fs/dev.h" +extern size_t iac_read(void *buf, size_t bufsize); +extern size_t iac_write(const void *buf, size_t bufsize); + // this file handles major device number 1, minor device numbers are mapped in table below struct dev_ops *mem_devs[256] = { // [1] = &prog_mem_dev, @@ -17,6 +20,8 @@ struct dev_ops *mem_devs[256] = { // [10] = &aio_dev, // [11] = &kmsg_dev, // [12] = &oldmem_dev, // replaced by /proc/vmcore + [99] = &iac_dev, + }; // dispatch device for major device 1 @@ -36,6 +41,21 @@ struct dev_ops mem_dev = { }; // begin inline devices +static int iac_open(int UNUSED(major), int UNUSED(minor), struct fd *UNUSED(fd)) { + return 0; +} +static ssize_t _iac_read(struct fd *UNUSED(fd), void *buf, size_t bufsize) { + return iac_read(buf, bufsize); // Defined in IOSGateway.m +} +static ssize_t _iac_write(struct fd *UNUSED(fd), const void *buf, size_t bufsize) { + return iac_write(buf, bufsize); // Defined in IOSGateway.m +} +struct dev_ops iac_dev = { + .open = iac_open, + .fd.read = _iac_read, + .fd.write = _iac_write, +}; + static int null_open(int UNUSED(major), int UNUSED(minor), struct fd *UNUSED(fd)) { return 0; } diff --git a/fs/mem.h b/fs/mem.h index 67291844a6..fc7a1f763c 100644 --- a/fs/mem.h +++ b/fs/mem.h @@ -9,6 +9,7 @@ extern struct dev_ops null_dev, zero_dev, full_dev, - random_dev; + random_dev, + iac_dev; #endif