@@ -0,0 +1,180 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVCommandDelegateImpl.h"
#import "CDVJSON_private.h"
#import "CDVCommandQueue.h"
#import "CDVPluginResult.h"
#import "CDVViewController.h"

@implementation CDVCommandDelegateImpl

- (id)initWithViewController:(CDVViewController*)viewController
{
self = [super init];
if (self != nil) {
_viewController = viewController;
_commandQueue = _viewController.commandQueue;

NSError* err = nil;
_callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9._-]" options:0 error:&err];
if (err != nil) {
// Couldn't initialize Regex
NSLog(@"Error: Couldn't initialize regex");
_callbackIdPattern = nil;
}
}
return self;
}

- (NSString*)pathForResource:(NSString*)resourcepath
{
NSBundle* mainBundle = [NSBundle mainBundle];
NSMutableArray* directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]];
NSString* filename = [directoryParts lastObject];

[directoryParts removeLastObject];

NSString* directoryPartsJoined = [directoryParts componentsJoinedByString:@"/"];
NSString* directoryStr = _viewController.wwwFolderName;

if ([directoryPartsJoined length] > 0) {
directoryStr = [NSString stringWithFormat:@"%@/%@", _viewController.wwwFolderName, [directoryParts componentsJoinedByString:@"/"]];
}

return [mainBundle pathForResource:filename ofType:@"" inDirectory:directoryStr];
}

- (void)flushCommandQueueWithDelayedJs
{
_delayResponses = YES;
[_commandQueue executePending];
_delayResponses = NO;
}

- (void)evalJsHelper2:(NSString*)js
{
CDV_EXEC_LOG(@"Exec: evalling: %@", [js substringToIndex:MIN([js length], 160)]);
NSString* commandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:js];
if ([commandsJSON length] > 0) {
CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by chaining.");
}

[_commandQueue enqueueCommandBatch:commandsJSON];
[_commandQueue executePending];
}

- (void)evalJsHelper:(NSString*)js
{
// Cycle the run-loop before executing the JS.
// For _delayResponses -
// This ensures that we don't eval JS during the middle of an existing JS
// function (possible since UIWebViewDelegate callbacks can be synchronous).
// For !isMainThread -
// It's a hard error to eval on the non-UI thread.
// For !_commandQueue.currentlyExecuting -
// This works around a bug where sometimes alerts() within callbacks can cause
// dead-lock.
// If the commandQueue is currently executing, then we know that it is safe to
// execute the callback immediately.
// Using (dispatch_get_main_queue()) does *not* fix deadlocks for some reason,
// but performSelectorOnMainThread: does.
if (_delayResponses || ![NSThread isMainThread] || !_commandQueue.currentlyExecuting) {
[self performSelectorOnMainThread:@selector(evalJsHelper2:) withObject:js waitUntilDone:NO];
} else {
[self evalJsHelper2:js];
}
}

- (BOOL)isValidCallbackId:(NSString*)callbackId
{
if ((callbackId == nil) || (_callbackIdPattern == nil)) {
return NO;
}

// Disallow if too long or if any invalid characters were found.
if (([callbackId length] > 100) || [_callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
return NO;
}
return YES;
}

- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId
{
CDV_EXEC_LOG(@"Exec(%@): Sending result. Status=%@", callbackId, result.status);
// This occurs when there is are no win/fail callbacks for the call.
if ([@"INVALID" isEqualToString : callbackId]) {
return;
}
// This occurs when the callback id is malformed.
if (![self isValidCallbackId:callbackId]) {
NSLog(@"Invalid callback id received by sendPluginResult");
return;
}
int status = [result.status intValue];
BOOL keepCallback = [result.keepCallback boolValue];
NSString* argumentsAsJSON = [result argumentsAsJSON];

NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)", callbackId, status, argumentsAsJSON, keepCallback];

[self evalJsHelper:js];
}

- (void)evalJs:(NSString*)js
{
[self evalJs:js scheduledOnRunLoop:YES];
}

- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop
{
js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})", js];
if (scheduledOnRunLoop) {
[self evalJsHelper:js];
} else {
[self evalJsHelper2:js];
}
}

- (id)getCommandInstance:(NSString*)pluginName
{
return [_viewController getCommandInstance:pluginName];
}

- (void)runInBackground:(void (^)())block
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}

- (NSString*)userAgent
{
return [_viewController userAgent];
}

- (BOOL)URLIsWhitelisted:(NSURL*)url
{
return ![_viewController.whitelist schemeIsAllowed:[url scheme]] ||
[_viewController.whitelist URLIsAllowed:url logFailure:NO];
}

- (NSDictionary*)settings
{
return _viewController.settings;
}

@end
@@ -0,0 +1,40 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>

@class CDVInvokedUrlCommand;
@class CDVViewController;

@interface CDVCommandQueue : NSObject

@property (nonatomic, readonly) BOOL currentlyExecuting;

- (id)initWithViewController:(CDVViewController*)viewController;
- (void)dispose;

- (void)resetRequestId;
- (void)enqueueCommandBatch:(NSString*)batchJSON;

- (void)processXhrExecBridgePoke:(NSNumber*)requestId;
- (void)fetchCommandsFromJs;
- (void)executePending;
- (BOOL)execute:(CDVInvokedUrlCommand*)command;

@end
@@ -0,0 +1,211 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#include <objc/message.h>
#import "CDV.h"
#import "CDVCommandQueue.h"
#import "CDVViewController.h"
#import "CDVCommandDelegateImpl.h"
#import "CDVJSON_private.h"

// Parse JS on the main thread if it's shorter than this.
static const NSInteger JSON_SIZE_FOR_MAIN_THREAD = 4 * 1024; // Chosen arbitrarily.
// Execute multiple commands in one go until this many seconds have passed.
static const double MAX_EXECUTION_TIME = .008; // Half of a 60fps frame.

@interface CDVCommandQueue () {
NSInteger _lastCommandQueueFlushRequestId;
__weak CDVViewController* _viewController;
NSMutableArray* _queue;
NSTimeInterval _startExecutionTime;
}
@end

@implementation CDVCommandQueue

- (BOOL)currentlyExecuting
{
return _startExecutionTime > 0;
}

- (id)initWithViewController:(CDVViewController*)viewController
{
self = [super init];
if (self != nil) {
_viewController = viewController;
_queue = [[NSMutableArray alloc] init];
}
return self;
}

- (void)dispose
{
// TODO(agrieve): Make this a zeroing weak ref once we drop support for 4.3.
_viewController = nil;
}

- (void)resetRequestId
{
_lastCommandQueueFlushRequestId = 0;
}

- (void)enqueueCommandBatch:(NSString*)batchJSON
{
if ([batchJSON length] > 0) {
NSMutableArray* commandBatchHolder = [[NSMutableArray alloc] init];
[_queue addObject:commandBatchHolder];
if ([batchJSON length] < JSON_SIZE_FOR_MAIN_THREAD) {
[commandBatchHolder addObject:[batchJSON cdv_JSONObject]];
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
NSMutableArray* result = [batchJSON cdv_JSONObject];
@synchronized(commandBatchHolder) {
[commandBatchHolder addObject:result];
}
[self performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
});
}
}
}

- (void)processXhrExecBridgePoke:(NSNumber*)requestId
{
NSInteger rid = [requestId integerValue];

// An ID of 1 is a special case because that signifies the first request of
// the page. Since resetRequestId is called from webViewDidStartLoad, and the
// JS context at the time of webViewDidStartLoad is still that of the previous
// page, it's possible for requests from the previous page to come in after this
// point. We ignore these by enforcing that ID=1 be the first ID.
if ((_lastCommandQueueFlushRequestId == 0) && (rid != 1)) {
CDV_EXEC_LOG(@"Exec: Ignoring exec request from previous page.");
return;
}

// Use the request ID to determine if we've already flushed for this request.
// This is required only because the NSURLProtocol enqueues the same request
// multiple times.
if (rid > _lastCommandQueueFlushRequestId) {
_lastCommandQueueFlushRequestId = [requestId integerValue];
[self fetchCommandsFromJs];
[self executePending];
}
}

- (void)fetchCommandsFromJs
{
// Grab all the queued commands from the JS side.
NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:
@"cordova.require('cordova/exec').nativeFetchMessages()"];

CDV_EXEC_LOG(@"Exec: Flushed JS->native queue (hadCommands=%d).", [queuedCommandsJSON length] > 0);
[self enqueueCommandBatch:queuedCommandsJSON];
}

- (void)executePending
{
// Make us re-entrant-safe.
if (_startExecutionTime > 0) {
return;
}
@try {
_startExecutionTime = [NSDate timeIntervalSinceReferenceDate];

while ([_queue count] > 0) {
NSMutableArray* commandBatchHolder = _queue[0];
NSMutableArray* commandBatch = nil;
@synchronized(commandBatchHolder) {
// If the next-up command is still being decoded, wait for it.
if ([commandBatchHolder count] == 0) {
break;
}
commandBatch = commandBatchHolder[0];
}

while ([commandBatch count] > 0) {
@autoreleasepool {
// Execute the commands one-at-a-time.
NSArray* jsonEntry = [commandBatch dequeue];
if ([commandBatch count] == 0) {
[_queue removeObjectAtIndex:0];
}
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);

if (![self execute:command]) {
#ifdef DEBUG
NSString* commandJson = [jsonEntry cdv_JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat:@"%@[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;

DLog(@"FAILED pluginJSON = %@", commandString);
#endif
}
}

// Yield if we're taking too long.
if (([_queue count] > 0) && ([NSDate timeIntervalSinceReferenceDate] - _startExecutionTime > MAX_EXECUTION_TIME)) {
[self performSelector:@selector(executePending) withObject:nil afterDelay:0];
return;
}
}
}
} @finally
{
_startExecutionTime = 0;
}
}

- (BOOL)execute:(CDVInvokedUrlCommand*)command
{
if ((command.className == nil) || (command.methodName == nil)) {
NSLog(@"ERROR: Classname and/or methodName not found for command.");
return NO;
}

// Fetch an instance of this class
CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className];

if (!([obj isKindOfClass:[CDVPlugin class]])) {
NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className);
return NO;
}
BOOL retVal = YES;
double started = [[NSDate date] timeIntervalSince1970] * 1000.0;
// Find the proper selector to call.
NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName];
SEL normalSelector = NSSelectorFromString(methodName);
if ([obj respondsToSelector:normalSelector]) {
// [obj performSelector:normalSelector withObject:command];
((void (*)(id, SEL, id))objc_msgSend)(obj, normalSelector, command);
} else {
// There's no method to call, so throw an error.
NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className);
retVal = NO;
}
double elapsed = [[NSDate date] timeIntervalSince1970] * 1000.0 - started;
if (elapsed > 10) {
NSLog(@"THREAD WARNING: ['%@'] took '%f' ms. Plugin should use a background thread.", command.className, elapsed);
}
return retVal;
}

@end
@@ -0,0 +1,31 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

@interface CDVConfigParser : NSObject <NSXMLParserDelegate>
{
NSString* featureName;
}

@property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSMutableArray* whitelistHosts;
@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readonly, strong) NSString* startPage;

@end
@@ -0,0 +1,88 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVConfigParser.h"

@interface CDVConfigParser ()

@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) NSMutableArray* whitelistHosts;
@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSString* startPage;

@end

@implementation CDVConfigParser

@synthesize pluginsDict, settings, whitelistHosts, startPage, startupPluginNames;

- (id)init
{
self = [super init];
if (self != nil) {
self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30];
self.settings = [[NSMutableDictionary alloc] initWithCapacity:30];
self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:30];
[self.whitelistHosts addObject:@"file:///*"];
[self.whitelistHosts addObject:@"content:///*"];
[self.whitelistHosts addObject:@"data:///*"];
self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8];
featureName = nil;
}
return self;
}

- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict
{
if ([elementName isEqualToString:@"preference"]) {
settings[[attributeDict[@"name"] lowercaseString]] = attributeDict[@"value"];
} else if ([elementName isEqualToString:@"feature"]) { // store feature name to use with correct parameter set
featureName = [attributeDict[@"name"] lowercaseString];
} else if ((featureName != nil) && [elementName isEqualToString:@"param"]) {
NSString* paramName = [attributeDict[@"name"] lowercaseString];
id value = attributeDict[@"value"];
if ([paramName isEqualToString:@"ios-package"]) {
pluginsDict[featureName] = value;
}
BOOL paramIsOnload = ([paramName isEqualToString:@"onload"] && [@"true" isEqualToString : value]);
BOOL attribIsOnload = [@"true" isEqualToString :[attributeDict[@"onload"] lowercaseString]];
if (paramIsOnload || attribIsOnload) {
[self.startupPluginNames addObject:featureName];
}
} else if ([elementName isEqualToString:@"access"]) {
[whitelistHosts addObject:attributeDict[@"origin"]];
} else if ([elementName isEqualToString:@"content"]) {
self.startPage = attributeDict[@"src"];
}
}

- (void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName
{
if ([elementName isEqualToString:@"feature"]) { // no longer handling a feature so release
featureName = nil;
}
}

- (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError
{
NSAssert(NO, @"config.xml parse error line %ld col %ld", (long)[parser lineNumber], (long)[parser columnNumber]);
}

@end
@@ -0,0 +1,25 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
@@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVPlugin.h"

@interface CDVHandleOpenURL : CDVPlugin

@property (nonatomic, strong) NSURL* url;
@property (nonatomic, assign) BOOL pageLoaded;

@end

@@ -0,0 +1,74 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVHandleOpenURL.h"
#import "CDV.h"

@implementation CDVHandleOpenURL

- (void)pluginInitialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationLaunchedWithUrl:) name:CDVPluginHandleOpenURLNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationPageDidLoad:) name:CDVPageDidLoadNotification object:nil];
}

- (void)applicationLaunchedWithUrl:(NSNotification*)notification
{
NSURL *url = [notification object];
self.url = url;

// warm-start handler
if (self.pageLoaded) {
[self processOpenUrl:self.url pageLoaded:YES];
self.url = nil;
}
}

- (void)applicationPageDidLoad:(NSNotification*)notification
{
// cold-start handler

self.pageLoaded = YES;

if (self.url) {
[self processOpenUrl:self.url pageLoaded:YES];
self.url = nil;
}
}

- (void)processOpenUrl:(NSURL*)url pageLoaded:(BOOL)pageLoaded
{
if (!pageLoaded) {
// query the webview for readystate
NSString* readyState = [self.webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
pageLoaded = [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
}

if (pageLoaded) {
// calls into javascript global function 'handleOpenURL'
NSString* jsString = [NSString stringWithFormat:@"document.addEventListener('deviceready',function(){if (typeof handleOpenURL === 'function') { handleOpenURL(\"%@\");}});", url];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
} else {
// save for when page has loaded
self.url = url;
}
}


@end
@@ -0,0 +1,52 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>

@interface CDVInvokedUrlCommand : NSObject {
NSString* _callbackId;
NSString* _className;
NSString* _methodName;
NSArray* _arguments;
}

@property (nonatomic, readonly) NSArray* arguments;
@property (nonatomic, readonly) NSString* callbackId;
@property (nonatomic, readonly) NSString* className;
@property (nonatomic, readonly) NSString* methodName;

+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry;

- (id)initWithArguments:(NSArray*)arguments
callbackId:(NSString*)callbackId
className:(NSString*)className
methodName:(NSString*)methodName;

- (id)initFromJson:(NSArray*)jsonEntry;

// Returns the argument at the given index.
// If index >= the number of arguments, returns nil.
// If the argument at the given index is NSNull, returns nil.
- (id)argumentAtIndex:(NSUInteger)index;
// Same as above, but returns defaultValue instead of nil.
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue;
// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass;

@end
@@ -0,0 +1,117 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVInvokedUrlCommand.h"
#import "CDVJSON_private.h"
#import "NSData+Base64.h"

@implementation CDVInvokedUrlCommand

@synthesize arguments = _arguments;
@synthesize callbackId = _callbackId;
@synthesize className = _className;
@synthesize methodName = _methodName;

+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry
{
return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry];
}

- (id)initFromJson:(NSArray*)jsonEntry
{
id tmp = [jsonEntry objectAtIndex:0];
NSString* callbackId = tmp == [NSNull null] ? nil : tmp;
NSString* className = [jsonEntry objectAtIndex:1];
NSString* methodName = [jsonEntry objectAtIndex:2];
NSMutableArray* arguments = [jsonEntry objectAtIndex:3];

return [self initWithArguments:arguments
callbackId:callbackId
className:className
methodName:methodName];
}

- (id)initWithArguments:(NSArray*)arguments
callbackId:(NSString*)callbackId
className:(NSString*)className
methodName:(NSString*)methodName
{
self = [super init];
if (self != nil) {
_arguments = arguments;
_callbackId = callbackId;
_className = className;
_methodName = methodName;
}
[self massageArguments];
return self;
}

- (void)massageArguments
{
NSMutableArray* newArgs = nil;

for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) {
id arg = [_arguments objectAtIndex:i];
if (![arg isKindOfClass:[NSDictionary class]]) {
continue;
}
NSDictionary* dict = arg;
NSString* type = [dict objectForKey:@"CDVType"];
if (!type || ![type isEqualToString:@"ArrayBuffer"]) {
continue;
}
NSString* data = [dict objectForKey:@"data"];
if (!data) {
continue;
}
if (newArgs == nil) {
newArgs = [NSMutableArray arrayWithArray:_arguments];
_arguments = newArgs;
}
[newArgs replaceObjectAtIndex:i withObject:[NSData cdv_dataFromBase64String:data]];
}
}

- (id)argumentAtIndex:(NSUInteger)index
{
return [self argumentAtIndex:index withDefault:nil];
}

- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue
{
return [self argumentAtIndex:index withDefault:defaultValue andClass:nil];
}

- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass
{
if (index >= [_arguments count]) {
return defaultValue;
}
id ret = [_arguments objectAtIndex:index];
if (ret == [NSNull null]) {
ret = defaultValue;
}
if ((aClass != nil) && ![ret isKindOfClass:aClass]) {
ret = defaultValue;
}
return ret;
}

@end
@@ -0,0 +1,37 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVAvailabilityDeprecated.h"

@interface NSArray (CDVJSONSerializing)
- (NSString*)JSONString CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");

@end

@interface NSDictionary (CDVJSONSerializing)
- (NSString*)JSONString CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");

@end

@interface NSString (CDVJSONSerializing)
- (id)JSONObject CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");

- (id)JSONFragment CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");

@end
@@ -0,0 +1,52 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVJSON_private.h"

@implementation NSArray (CDVJSONSerializing)

- (NSString*)JSONString
{
return [self cdv_JSONString];
}

@end

@implementation NSDictionary (CDVJSONSerializing)

- (NSString*)JSONString
{
return [self cdv_JSONString];
}

@end

@implementation NSString (CDVJSONSerializing)

- (id)JSONObject
{
return [self cdv_JSONObject];
}

- (id)JSONFragment
{
return [self cdv_JSONFragment];
}

@end
@@ -0,0 +1,31 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

@interface NSArray (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString;
@end

@interface NSDictionary (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString;
@end

@interface NSString (CDVJSONSerializingPrivate)
- (id)cdv_JSONObject;
- (id)cdv_JSONFragment;
@end
@@ -0,0 +1,91 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVJSON_private.h"
#import <Foundation/NSJSONSerialization.h>

@implementation NSArray (CDVJSONSerializingPrivate)

- (NSString*)cdv_JSONString
{
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
options:0
error:&error];

if (error != nil) {
NSLog(@"NSArray JSONString error: %@", [error localizedDescription]);
return nil;
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}

@end

@implementation NSDictionary (CDVJSONSerializingPrivate)

- (NSString*)cdv_JSONString
{
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
options:NSJSONWritingPrettyPrinted
error:&error];

if (error != nil) {
NSLog(@"NSDictionary JSONString error: %@", [error localizedDescription]);
return nil;
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}

@end

@implementation NSString (CDVJSONSerializingPrivate)

- (id)cdv_JSONObject
{
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingMutableContainers
error:&error];

if (error != nil) {
NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
}

return object;
}

- (id)cdv_JSONFragment
{
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:&error];

if (error != nil) {
NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
}

return object;
}

@end
@@ -0,0 +1,50 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVPlugin.h"

#define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain"
#define kCDVLocalStorageFileOperationError 1

@interface CDVLocalStorage : CDVPlugin

@property (nonatomic, readonly, strong) NSMutableArray* backupInfo;

- (BOOL)shouldBackup;
- (BOOL)shouldRestore;
- (void)backup:(CDVInvokedUrlCommand*)command;
- (void)restore:(CDVInvokedUrlCommand*)command;

+ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType;
// Visible for testing.
+ (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict
bundlePath:(NSString*)bundlePath
fileManager:(NSFileManager*)fileManager;
@end

@interface CDVBackupInfo : NSObject

@property (nonatomic, copy) NSString* original;
@property (nonatomic, copy) NSString* backup;
@property (nonatomic, copy) NSString* label;

- (BOOL)shouldBackup;
- (BOOL)shouldRestore;

@end

Large diffs are not rendered by default.

@@ -0,0 +1,67 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CDVPluginResult.h"
#import "NSMutableArray+QueueAdditions.h"
#import "CDVCommandDelegate.h"

extern NSString* const CDVPageDidLoadNotification;
extern NSString* const CDVPluginHandleOpenURLNotification;
extern NSString* const CDVPluginResetNotification;
extern NSString* const CDVLocalNotification;
extern NSString* const CDVRemoteNotification;
extern NSString* const CDVRemoteNotificationError;

@interface CDVPlugin : NSObject {}

@property (nonatomic, weak) UIWebView* webView;
@property (nonatomic, weak) UIViewController* viewController;
@property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate;

@property (readonly, assign) BOOL hasPendingOperation;

- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView;
- (void)pluginInitialize;

- (void)handleOpenURL:(NSNotification*)notification;
- (void)onAppTerminate;
- (void)onMemoryWarning;
- (void)onReset;
- (void)dispose;

/*
// see initWithWebView implementation
- (void) onPause {}
- (void) onResume {}
- (void) onOrientationWillChange {}
- (void) onOrientationDidChange {}
- (void)didReceiveLocalNotification:(NSNotification *)notification;
*/

- (id)appDelegate;

- (NSString*)writeJavascript:(NSString*)javascript CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of evalJs:. This will be removed in 4.0.0");

- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of sendPluginResult:callbackId. This will be removed in 4.0.0");

- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of sendPluginResult:callbackId. This will be removed in 4.0.0");

@end
@@ -0,0 +1,154 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVPlugin.h"

NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification";
NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification";
NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification";
NSString* const CDVLocalNotification = @"CDVLocalNotification";
NSString* const CDVRemoteNotification = @"CDVRemoteNotification";
NSString* const CDVRemoteNotificationError = @"CDVRemoteNotificationError";

@interface CDVPlugin ()

@property (readwrite, assign) BOOL hasPendingOperation;

@end

@implementation CDVPlugin
@synthesize webView, viewController, commandDelegate, hasPendingOperation;

// Do not override these methods. Use pluginInitialize instead.
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings
{
return [self initWithWebView:theWebView];
}

- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:theWebView];

self.webView = theWebView;
}
return self;
}

- (void)pluginInitialize
{
// You can listen to more app notifications, see:
// http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4

// NOTE: if you want to use these, make sure you uncomment the corresponding notification handler

// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];

// Added in 2.3.0
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil];

// Added in 2.5.0
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];
}

- (void)dispose
{
viewController = nil;
commandDelegate = nil;
webView = nil;
}

/*
// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts
- (void) onPause {}
- (void) onResume {}
- (void) onOrientationWillChange {}
- (void) onOrientationDidChange {}
*/

/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
- (void)handleOpenURL:(NSNotification*)notification
{
// override to handle urls sent to your app
// register your url schemes in your App-Info.plist

NSURL* url = [notification object];

if ([url isKindOfClass:[NSURL class]]) {
/* Do your thing! */
}
}

/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
- (void)onAppTerminate
{
// override this if you need to do any cleanup on app exit
}

- (void)onMemoryWarning
{
// override to remove caches, etc
}

- (void)onReset
{
// Override to cancel any long-running requests when the WebView navigates or refreshes.
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock:
}

- (id)appDelegate
{
return [[UIApplication sharedApplication] delegate];
}

- (NSString*)writeJavascript:(NSString*)javascript
{
return [self.webView stringByEvaluatingJavaScriptFromString:javascript];
}

- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId
{
[self.commandDelegate evalJs:[pluginResult toSuccessCallbackString:callbackId]];
return @"";
}

- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId
{
[self.commandDelegate evalJs:[pluginResult toErrorCallbackString:callbackId]];
return @"";
}

// default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything.
// - (void)didReceiveLocalNotification:(NSNotification *)notification
// {
// // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification
// }

@end
@@ -0,0 +1,71 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

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

typedef enum {
CDVCommandStatus_NO_RESULT = 0,
CDVCommandStatus_OK,
CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION,
CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION,
CDVCommandStatus_INSTANTIATION_EXCEPTION,
CDVCommandStatus_MALFORMED_URL_EXCEPTION,
CDVCommandStatus_IO_EXCEPTION,
CDVCommandStatus_INVALID_ACTION,
CDVCommandStatus_JSON_EXCEPTION,
CDVCommandStatus_ERROR
} CDVCommandStatus;

@interface CDVPluginResult : NSObject {}

@property (nonatomic, strong, readonly) NSNumber* status;
@property (nonatomic, strong, readonly) id message;
@property (nonatomic, strong) NSNumber* keepCallback;
// This property can be used to scope the lifetime of another object. For example,
// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy.
@property (nonatomic, strong) id associatedObject;

- (CDVPluginResult*)init;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;

+ (void)setVerbose:(BOOL)verbose;
+ (BOOL)isVerbose;

- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback;

- (NSString*)argumentsAsJSON;

// These methods are used by the legacy plugin return result method
- (NSString*)toJSONString CDV_DEPRECATED(3.6, "Only used by toSuccessCallbackString and toErrorCallbackString which are deprecated. This will be removed in 4.0.0");

- (NSString*)toSuccessCallbackString:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate method sendPluginResult:callbackId instead. This will be removed in 4.0.0");

- (NSString*)toErrorCallbackString:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate method sendPluginResult:callbackId instead. This will be removed in 4.0.0");

@end
@@ -0,0 +1,224 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVPluginResult.h"
#import "CDVJSON_private.h"
#import "CDVDebug.h"
#import "NSData+Base64.h"

@interface CDVPluginResult ()

- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage;

@end

@implementation CDVPluginResult
@synthesize status, message, keepCallback, associatedObject;

static NSArray* org_apache_cordova_CommandStatusMsgs;

id messageFromArrayBuffer(NSData* data)
{
return @{
@"CDVType" : @"ArrayBuffer",
@"data" :[data cdv_base64EncodedString]
};
}

id massageMessage(id message)
{
if ([message isKindOfClass:[NSData class]]) {
return messageFromArrayBuffer(message);
}
return message;
}

id messageFromMultipart(NSArray* theMessages)
{
NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages];

for (NSUInteger i = 0; i < messages.count; ++i) {
[messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])];
}

return @{
@"CDVType" : @"MultiPart",
@"messages" : messages
};
}

+ (void)initialize
{
org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result",
@"OK",
@"Class not found",
@"Illegal access",
@"Instantiation error",
@"Malformed url",
@"IO error",
@"Invalid action",
@"JSON error",
@"Error",
nil];
}

- (CDVPluginResult*)init
{
return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil];
}

- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage
{
self = [super init];
if (self) {
status = [NSNumber numberWithInt:statusOrdinal];
message = theMessage;
keepCallback = [NSNumber numberWithBool:NO];
}
return self;
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal
{
return [[self alloc] initWithStatus:statusOrdinal message:nil];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages
{
return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)];
}

+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode
{
NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]};

return [[self alloc] initWithStatus:statusOrdinal message:errDict];
}

- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback
{
[self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]];
}

- (NSString*)argumentsAsJSON
{
id arguments = (self.message == nil ? [NSNull null] : self.message);
NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments];

NSString* argumentsJSON = [argumentsWrappedInArray cdv_JSONString];

argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)];

return argumentsJSON;
}

// These methods are used by the legacy plugin return result method
- (NSString*)toJSONString
{
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
self.status, @"status",
self.message ? self. message:[NSNull null], @"message",
self.keepCallback, @"keepCallback",
nil];

NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
NSString* resultString = nil;

if (error != nil) {
NSLog(@"toJSONString error: %@", [error localizedDescription]);
} else {
resultString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

if ([[self class] isVerbose]) {
NSLog(@"PluginResult:toJSONString - %@", resultString);
}
return resultString;
}

- (NSString*)toSuccessCallbackString:(NSString*)callbackId
{
NSString* successCB = [NSString stringWithFormat:@"cordova.callbackSuccess('%@',%@);", callbackId, [self toJSONString]];

if ([[self class] isVerbose]) {
NSLog(@"PluginResult toSuccessCallbackString: %@", successCB);
}
return successCB;
}

- (NSString*)toErrorCallbackString:(NSString*)callbackId
{
NSString* errorCB = [NSString stringWithFormat:@"cordova.callbackError('%@',%@);", callbackId, [self toJSONString]];

if ([[self class] isVerbose]) {
NSLog(@"PluginResult toErrorCallbackString: %@", errorCB);
}
return errorCB;
}

static BOOL gIsVerbose = NO;
+ (void)setVerbose:(BOOL)verbose
{
gIsVerbose = verbose;
}

+ (BOOL)isVerbose
{
return gIsVerbose;
}

@end
@@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>

@protocol CDVScreenOrientationDelegate <NSObject>

- (NSUInteger)supportedInterfaceOrientations;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (BOOL)shouldAutorotate;

@end
@@ -0,0 +1,22 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

// This file was emptied out in 3.6.0 release (July 2014).
// It will be deleted in a future release.
#import <CoreLocation/CoreLocation.h>
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>

@interface CDVTimer : NSObject

+ (void)start:(NSString*)name;
+ (void)stop:(NSString*)name;

@end
@@ -0,0 +1,123 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVTimer.h"

#pragma mark CDVTimerItem

@interface CDVTimerItem : NSObject

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSDate* started;
@property (nonatomic, strong) NSDate* ended;

- (void)log;

@end

@implementation CDVTimerItem

- (void)log
{
NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0);
}

@end

#pragma mark CDVTimer

@interface CDVTimer ()

@property (nonatomic, strong) NSMutableDictionary* items;

@end

@implementation CDVTimer

#pragma mark object methods

- (id)init
{
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:6];
}

return self;
}

- (void)add:(NSString*)name
{
if ([self.items objectForKey:[name lowercaseString]] == nil) {
CDVTimerItem* item = [CDVTimerItem new];
item.name = name;
item.started = [NSDate new];
[self.items setObject:item forKey:[name lowercaseString]];
} else {
NSLog(@"Timer called '%@' already exists.", name);
}
}

- (void)remove:(NSString*)name
{
CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]];

if (item != nil) {
item.ended = [NSDate new];
[item log];
[self.items removeObjectForKey:[name lowercaseString]];
} else {
NSLog(@"Timer called '%@' does not exist.", name);
}
}

- (void)removeAll
{
[self.items removeAllObjects];
}

#pragma mark class methods

+ (void)start:(NSString*)name
{
[[CDVTimer sharedInstance] add:name];
}

+ (void)stop:(NSString*)name
{
[[CDVTimer sharedInstance] remove:name];
}

+ (void)clearAll
{
[[CDVTimer sharedInstance] removeAll];
}

+ (CDVTimer*)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static CDVTimer* _sharedObject = nil;

dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});

return _sharedObject;
}

@end
@@ -0,0 +1,29 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

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

@class CDVViewController;

@interface CDVURLProtocol : NSURLProtocol {}

+ (void)registerViewController:(CDVViewController*)viewController;
+ (void)unregisterViewController:(CDVViewController*)viewController;
@end
@@ -0,0 +1,213 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <AssetsLibrary/ALAsset.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AssetsLibrary/ALAssetsLibrary.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "CDVURLProtocol.h"
#import "CDVCommandQueue.h"
#import "CDVWhitelist.h"
#import "CDVViewController.h"

static CDVWhitelist* gWhitelist = nil;
// Contains a set of NSNumbers of addresses of controllers. It doesn't store
// the actual pointer to avoid retaining.
static NSMutableSet* gRegisteredControllers = nil;

NSString* const kCDVAssetsLibraryPrefixes = @"assets-library://";

// Returns the registered view controller that sent the given request.
// If the user-agent is not from a UIWebView, or if it's from an unregistered one,
// then nil is returned.
static CDVViewController *viewControllerForRequest(NSURLRequest* request)
{
// The exec bridge explicitly sets the VC address in a header.
// This works around the User-Agent not being set for file: URLs.
NSString* addrString = [request valueForHTTPHeaderField:@"vc"];

if (addrString == nil) {
NSString* userAgent = [request valueForHTTPHeaderField:@"User-Agent"];
if (userAgent == nil) {
return nil;
}
NSUInteger bracketLocation = [userAgent rangeOfString:@"(" options:NSBackwardsSearch].location;
if (bracketLocation == NSNotFound) {
return nil;
}
addrString = [userAgent substringFromIndex:bracketLocation + 1];
}

long long viewControllerAddress = [addrString longLongValue];
@synchronized(gRegisteredControllers) {
if (![gRegisteredControllers containsObject:[NSNumber numberWithLongLong:viewControllerAddress]]) {
return nil;
}
}

return (__bridge CDVViewController*)(void*)viewControllerAddress;
}

@implementation CDVURLProtocol

+ (void)registerPGHttpURLProtocol {}

+ (void)registerURLProtocol {}

// Called to register the URLProtocol, and to make it away of an instance of
// a ViewController.
+ (void)registerViewController:(CDVViewController*)viewController
{
if (gRegisteredControllers == nil) {
[NSURLProtocol registerClass:[CDVURLProtocol class]];
gRegisteredControllers = [[NSMutableSet alloc] initWithCapacity:8];
// The whitelist doesn't change, so grab the first one and store it.
gWhitelist = viewController.whitelist;

// Note that we grab the whitelist from the first viewcontroller for now - but this will change
// when we allow a registered viewcontroller to have its own whitelist (e.g InAppBrowser)
// Differentiating the requests will be through the 'vc' http header below as used for the js->objc bridge.
// The 'vc' value is generated by casting the viewcontroller object to a (long long) value (see CDVViewController::webViewDidFinishLoad)
if (gWhitelist == nil) {
NSLog(@"WARNING: NO whitelist has been set in CDVURLProtocol.");
}
}

@synchronized(gRegisteredControllers) {
[gRegisteredControllers addObject:[NSNumber numberWithLongLong:(long long)viewController]];
}
}

+ (void)unregisterViewController:(CDVViewController*)viewController
{
@synchronized(gRegisteredControllers) {
[gRegisteredControllers removeObject:[NSNumber numberWithLongLong:(long long)viewController]];
}
}

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
NSURL* theUrl = [theRequest URL];
CDVViewController* viewController = viewControllerForRequest(theRequest);

if ([[theUrl absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) {
return YES;
} else if (viewController != nil) {
if ([[theUrl path] isEqualToString:@"/!gap_exec"]) {
NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];
NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];
if (requestId == nil) {
NSLog(@"!cordova request missing rc header");
return NO;
}
BOOL hasCmds = [queuedCommandsJSON length] > 0;
if (hasCmds) {
SEL sel = @selector(enqueueCommandBatch:);
[viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO];
[viewController.commandQueue performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
} else {
SEL sel = @selector(processXhrExecBridgePoke:);
[viewController.commandQueue performSelectorOnMainThread:sel withObject:[NSNumber numberWithInteger:[requestId integerValue]] waitUntilDone:NO];
}
// Returning NO here would be 20% faster, but it spams WebInspector's console with failure messages.
// If JS->Native bridge speed is really important for an app, they should use the iframe bridge.
// Returning YES here causes the request to come through canInitWithRequest two more times.
// For this reason, we return NO when cmds exist.
return !hasCmds;
}
// we only care about http and https connections.
// CORS takes care of http: trying to access file: URLs.
if ([gWhitelist schemeIsAllowed:[theUrl scheme]]) {
// if it FAILS the whitelist, we return TRUE, so we can fail the connection later
return ![gWhitelist URLIsAllowed:theUrl];
}
}

return NO;
}

+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
{
// NSLog(@"%@ received %@", self, NSStringFromSelector(_cmd));
return request;
}

- (void)startLoading
{
// NSLog(@"%@ received %@ - start", self, NSStringFromSelector(_cmd));
NSURL* url = [[self request] URL];

if ([[url path] isEqualToString:@"/!gap_exec"]) {
[self sendResponseWithResponseCode:200 data:nil mimeType:nil];
return;
} else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) {
ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
if (asset) {
// We have the asset! Get the data and send it along.
ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
Byte* buffer = (Byte*)malloc((unsigned long)[assetRepresentation size]);
NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:(NSUInteger)[assetRepresentation size] error:nil];
NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
[self sendResponseWithResponseCode:200 data:data mimeType:MIMEType];
} else {
// Retrieving the asset failed for some reason. Send an error.
[self sendResponseWithResponseCode:404 data:nil mimeType:nil];
}
};
ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
// Retrieving the asset failed for some reason. Send an error.
[self sendResponseWithResponseCode:401 data:nil mimeType:nil];
};

ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary assetForURL:url resultBlock:resultBlock failureBlock:failureBlock];
return;
}

NSString* body = [gWhitelist errorStringForURL:url];
[self sendResponseWithResponseCode:401 data:[body dataUsingEncoding:NSASCIIStringEncoding] mimeType:nil];
}

- (void)stopLoading
{
// do any cleanup here
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
{
return NO;
}

- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType
{
if (mimeType == nil) {
mimeType = @"text/plain";
}

NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : mimeType}];

[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
if (data != nil) {
[[self client] URLProtocol:self didLoadData:data];
}
[[self client] URLProtocolDidFinishLoading:self];
}

@end
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>

@interface CDVUserAgentUtil : NSObject
+ (NSString*)originalUserAgent;
+ (void)acquireLock:(void (^)(NSInteger lockToken))block;
+ (void)releaseLock:(NSInteger*)lockToken;
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken;
@end
@@ -0,0 +1,122 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVUserAgentUtil.h"

#import <UIKit/UIKit.h>

// #define VerboseLog NSLog
#define VerboseLog(...) do {} while (0)

static NSString* const kCdvUserAgentKey = @"Cordova-User-Agent";
static NSString* const kCdvUserAgentVersionKey = @"Cordova-User-Agent-Version";

static NSString* gOriginalUserAgent = nil;
static NSInteger gNextLockToken = 0;
static NSInteger gCurrentLockToken = 0;
static NSMutableArray* gPendingSetUserAgentBlocks = nil;

@implementation CDVUserAgentUtil

+ (NSString*)originalUserAgent
{
if (gOriginalUserAgent == nil) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppLocaleDidChange:)
name:NSCurrentLocaleDidChangeNotification object:nil];

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
NSString* localeStr = [[NSLocale currentLocale] localeIdentifier];
// Record the model since simulator can change it without re-install (CB-5420).
NSString* model = [UIDevice currentDevice].model;
NSString* systemAndLocale = [NSString stringWithFormat:@"%@ %@ %@", model, systemVersion, localeStr];

NSString* cordovaUserAgentVersion = [userDefaults stringForKey:kCdvUserAgentVersionKey];
gOriginalUserAgent = [userDefaults stringForKey:kCdvUserAgentKey];
BOOL cachedValueIsOld = ![systemAndLocale isEqualToString:cordovaUserAgentVersion];

if ((gOriginalUserAgent == nil) || cachedValueIsOld) {
UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
gOriginalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];

[userDefaults setObject:gOriginalUserAgent forKey:kCdvUserAgentKey];
[userDefaults setObject:systemAndLocale forKey:kCdvUserAgentVersionKey];

[userDefaults synchronize];
}
}
return gOriginalUserAgent;
}

+ (void)onAppLocaleDidChange:(NSNotification*)notification
{
// TODO: We should figure out how to update the user-agent of existing UIWebViews when this happens.
// Maybe use the PDF bug (noted in setUserAgent:).
gOriginalUserAgent = nil;
}

+ (void)acquireLock:(void (^)(NSInteger lockToken))block
{
if (gCurrentLockToken == 0) {
gCurrentLockToken = ++gNextLockToken;
VerboseLog(@"Gave lock %d", gCurrentLockToken);
block(gCurrentLockToken);
} else {
if (gPendingSetUserAgentBlocks == nil) {
gPendingSetUserAgentBlocks = [[NSMutableArray alloc] initWithCapacity:4];
}
VerboseLog(@"Waiting for lock");
[gPendingSetUserAgentBlocks addObject:block];
}
}

+ (void)releaseLock:(NSInteger*)lockToken
{
if (*lockToken == 0) {
return;
}
NSAssert(gCurrentLockToken == *lockToken, @"Got token %ld, expected %ld", (long)*lockToken, (long)gCurrentLockToken);

VerboseLog(@"Released lock %d", *lockToken);
if ([gPendingSetUserAgentBlocks count] > 0) {
void (^block)() = [gPendingSetUserAgentBlocks objectAtIndex:0];
[gPendingSetUserAgentBlocks removeObjectAtIndex:0];
gCurrentLockToken = ++gNextLockToken;
NSLog(@"Gave lock %ld", (long)gCurrentLockToken);
block(gCurrentLockToken);
} else {
gCurrentLockToken = 0;
}
*lockToken = 0;
}

+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken
{
NSAssert(gCurrentLockToken == lockToken, @"Got token %ld, expected %ld", (long)lockToken, (long)gCurrentLockToken);
VerboseLog(@"User-Agent set to: %@", value);

// Setting the UserAgent must occur before a UIWebView is instantiated.
// It is read per instantiation, so it does not affect previously created views.
// Except! When a PDF is loaded, all currently active UIWebViews reload their
// User-Agent from the NSUserDefaults some time after the DidFinishLoad of the PDF bah!
NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:value, @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
}

@end
@@ -0,0 +1,84 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <UIKit/UIKit.h>
#import <Foundation/NSJSONSerialization.h>
#import "CDVAvailability.h"
#import "CDVInvokedUrlCommand.h"
#import "CDVCommandDelegate.h"
#import "CDVCommandQueue.h"
#import "CDVWhitelist.h"
#import "CDVScreenOrientationDelegate.h"
#import "CDVPlugin.h"

@interface CDVViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{
@protected
id <CDVCommandDelegate> _commandDelegate;
@protected
CDVCommandQueue* _commandQueue;
NSString* _userAgent;
}

@property (nonatomic, strong) IBOutlet UIWebView* webView;

@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readonly, strong) NSDictionary* pluginsMap;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSXMLParser* configParser;
@property (nonatomic, readonly, strong) CDVWhitelist* whitelist; // readonly for public
@property (nonatomic, readonly, assign) BOOL loadFromString;

@property (nonatomic, readwrite, copy) NSString* wwwFolderName;
@property (nonatomic, readwrite, copy) NSString* startPage;
@property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue;
@property (nonatomic, readonly, strong) id <CDVCommandDelegate> commandDelegate;

/**
The complete user agent that Cordova will use when sending web requests.
*/
@property (nonatomic, readonly) NSString* userAgent;

/**
The base user agent data that Cordova will use to build its user agent. If this
property isn't set, Cordova will use the standard web view user agent as its
base.
*/
@property (nonatomic, readwrite, copy) NSString* baseUserAgent;

+ (NSDictionary*)getBundlePlist:(NSString*)plistName;
+ (NSString*)applicationDocumentsDirectory;

- (void)printMultitaskingInfo;
- (void)createGapView;
- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds;

- (void)javascriptAlert:(NSString*)text;
- (NSString*)appURLScheme;

- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations;
- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation;

- (id)getCommandInstance:(NSString*)pluginName;
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className;
- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName;

- (BOOL)URLisAllowed:(NSURL*)url;
- (void)processOpenUrl:(NSURL*)url;

@end

Large diffs are not rendered by default.

@@ -0,0 +1,41 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <UIKit/UIKit.h>
#import "CDVAvailability.h"

/**
* Distinguishes top-level navigations from sub-frame navigations.
* shouldStartLoadWithRequest is called for every request, but didStartLoad
* and didFinishLoad is called only for top-level navigations.
* Relevant bug: CB-2389
*/
@interface CDVWebViewDelegate : NSObject <UIWebViewDelegate>{
__weak NSObject <UIWebViewDelegate>* _delegate;
NSInteger _loadCount;
NSInteger _state;
NSInteger _curLoadToken;
NSInteger _loadStartPollCount;
}

- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate;

- (BOOL)request:(NSURLRequest*)newRequest isEqualToRequestAfterStrippingFragments:(NSURLRequest*)originalRequest;

@end

Large diffs are not rendered by default.

@@ -0,0 +1,34 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import <Foundation/Foundation.h>

extern NSString* const kCDVDefaultWhitelistRejectionString;

@interface CDVWhitelist : NSObject

@property (nonatomic, copy) NSString* whitelistRejectionFormatString;

- (id)initWithArray:(NSArray*)array;
- (BOOL)schemeIsAllowed:(NSString*)scheme;
- (BOOL)URLIsAllowed:(NSURL*)url;
- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure;
- (NSString*)errorStringForURL:(NSURL*)url;

@end
@@ -0,0 +1,285 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "CDVWhitelist.h"

NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme";

@interface CDVWhitelistPattern : NSObject {
@private
NSRegularExpression* _scheme;
NSRegularExpression* _host;
NSNumber* _port;
NSRegularExpression* _path;
}

+ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards;
- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path;
- (bool)matches:(NSURL*)url;

@end

@implementation CDVWhitelistPattern

+ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards
{
NSString* regex = [NSRegularExpression escapedPatternForString:pattern];

if (allowWildcards) {
regex = [regex stringByReplacingOccurrencesOfString:@"\\*" withString:@".*"];

/* [NSURL path] has the peculiarity that a trailing slash at the end of a path
* will be omitted. This regex tweak compensates for that.
*/
if ([regex hasSuffix:@"\\/.*"]) {
regex = [NSString stringWithFormat:@"%@(\\/.*)?", [regex substringToIndex:([regex length] - 4)]];
}
}
return [NSString stringWithFormat:@"%@$", regex];
}

- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path
{
self = [super init]; // Potentially change "self"
if (self) {
if ((scheme == nil) || [scheme isEqualToString:@"*"]) {
_scheme = nil;
} else {
_scheme = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:scheme allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
}
if ([host isEqualToString:@"*"]) {
_host = nil;
} else if ([host hasPrefix:@"*."]) {
_host = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"([a-z0-9.-]*\\.)?%@", [CDVWhitelistPattern regexFromPattern:[host substringFromIndex:2] allowWildcards:false]] options:NSRegularExpressionCaseInsensitive error:nil];
} else {
_host = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:host allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
}
if ((port == nil) || [port isEqualToString:@"*"]) {
_port = nil;
} else {
_port = [[NSNumber alloc] initWithInteger:[port integerValue]];
}
if ((path == nil) || [path isEqualToString:@"/*"]) {
_path = nil;
} else {
_path = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:path allowWildcards:YES] options:0 error:nil];
}
}
return self;
}

- (bool)matches:(NSURL*)url
{
return (_scheme == nil || [_scheme numberOfMatchesInString:[url scheme] options:NSMatchingAnchored range:NSMakeRange(0, [[url scheme] length])]) &&
(_host == nil || [_host numberOfMatchesInString:[url host] options:NSMatchingAnchored range:NSMakeRange(0, [[url host] length])]) &&
(_port == nil || [[url port] isEqualToNumber:_port]) &&
(_path == nil || [_path numberOfMatchesInString:[url path] options:NSMatchingAnchored range:NSMakeRange(0, [[url path] length])])
;
}

@end

@interface CDVWhitelist ()

@property (nonatomic, readwrite, strong) NSMutableArray* whitelist;
@property (nonatomic, readwrite, strong) NSMutableSet* permittedSchemes;

- (void)addWhiteListEntry:(NSString*)pattern;

@end

@implementation CDVWhitelist

@synthesize whitelist, permittedSchemes, whitelistRejectionFormatString;

- (id)initWithArray:(NSArray*)array
{
self = [super init];
if (self) {
self.whitelist = [[NSMutableArray alloc] init];
self.permittedSchemes = [[NSMutableSet alloc] init];
self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;

for (NSString* pattern in array) {
[self addWhiteListEntry:pattern];
}
}
return self;
}

- (BOOL)isIPv4Address:(NSString*)externalHost
{
// an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255.
// for our purposes, b can also be the wildcard character '*'

// we could use a regex to solve this problem but then I would have two problems
// anyways, this is much clearer and maintainable
NSArray* octets = [externalHost componentsSeparatedByString:@"."];
NSUInteger num_octets = [octets count];

// quick check
if (num_octets != 4) {
return NO;
}

// restrict number parsing to 0-255
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]];
[numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]];

// iterate through each octet, and test for a number between 0-255 or if it equals '*'
for (NSUInteger i = 0; i < num_octets; ++i) {
NSString* octet = [octets objectAtIndex:i];

if ([octet isEqualToString:@"*"]) { // passes - check next octet
continue;
} else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return
return NO;
}
}

return YES;
}

- (void)addWhiteListEntry:(NSString*)origin
{
if (self.whitelist == nil) {
return;
}

if ([origin isEqualToString:@"*"]) {
NSLog(@"Unlimited access to network resources");
self.whitelist = nil;
self.permittedSchemes = nil;
} else { // specific access
NSRegularExpression* parts = [NSRegularExpression regularExpressionWithPattern:@"^((\\*|[A-Za-z-]+)://)?(((\\*\\.)?[^*/:]+)|\\*)?(:(\\d+))?(/.*)?" options:0 error:nil];
NSTextCheckingResult* m = [parts firstMatchInString:origin options:NSMatchingAnchored range:NSMakeRange(0, [origin length])];
if (m != nil) {
NSRange r;
NSString* scheme = nil;
r = [m rangeAtIndex:2];
if (r.location != NSNotFound) {
scheme = [origin substringWithRange:r];
}

NSString* host = nil;
r = [m rangeAtIndex:3];
if (r.location != NSNotFound) {
host = [origin substringWithRange:r];
}

// Special case for two urls which are allowed to have empty hosts
if (([scheme isEqualToString:@"file"] || [scheme isEqualToString:@"content"]) && (host == nil)) {
host = @"*";
}

NSString* port = nil;
r = [m rangeAtIndex:7];
if (r.location != NSNotFound) {
port = [origin substringWithRange:r];
}

NSString* path = nil;
r = [m rangeAtIndex:8];
if (r.location != NSNotFound) {
path = [origin substringWithRange:r];
}

if (scheme == nil) {
// XXX making it stupid friendly for people who forget to include protocol/SSL
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"http" host:host port:port path:path]];
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"https" host:host port:port path:path]];
} else {
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:scheme host:host port:port path:path]];
}

if (self.permittedSchemes != nil) {
if ([scheme isEqualToString:@"*"]) {
self.permittedSchemes = nil;
} else if (scheme != nil) {
[self.permittedSchemes addObject:scheme];
}
}
}
}
}

- (BOOL)schemeIsAllowed:(NSString*)scheme
{
if ([scheme isEqualToString:@"http"] ||
[scheme isEqualToString:@"https"] ||
[scheme isEqualToString:@"ftp"] ||
[scheme isEqualToString:@"ftps"]) {
return YES;
}

return (self.permittedSchemes == nil) || [self.permittedSchemes containsObject:scheme];
}

- (BOOL)URLIsAllowed:(NSURL*)url
{
return [self URLIsAllowed:url logFailure:YES];
}

- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure
{
// Shortcut acceptance: Are all urls whitelisted ("*" in whitelist)?
if (whitelist == nil) {
return YES;
}

// Shortcut rejection: Check that the scheme is supported
NSString* scheme = [[url scheme] lowercaseString];
if (![self schemeIsAllowed:scheme]) {
if (logFailure) {
NSLog(@"%@", [self errorStringForURL:url]);
}
return NO;
}

// http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list
if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) {
NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@%@", kCDVDefaultSchemeName, [url host], [[url path] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
// If it is allowed, we are done. If not, continue to check for the actual scheme-specific list
if ([self URLIsAllowed:newUrl logFailure:NO]) {
return YES;
}
}

// Check the url against patterns in the whitelist
for (CDVWhitelistPattern* p in self.whitelist) {
if ([p matches:url]) {
return YES;
}
}

if (logFailure) {
NSLog(@"%@", [self errorStringForURL:url]);
}
// if we got here, the url host is not in the white-list, do nothing
return NO;
}

- (NSString*)errorStringForURL:(NSURL*)url
{
return [NSString stringWithFormat:self.whitelistRejectionFormatString, [url absoluteString]];
}

@end
@@ -0,0 +1,27 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

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

@interface NSArray (Comparisons)

- (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault CDV_DEPRECATED(3.8 .0, "Use [command argumentAtIndex] instead.");

@end
@@ -0,0 +1,43 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

#import "NSArray+Comparisons.h"

@implementation NSArray (Comparisons)

- (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault
{
id obj = nil;

@try {
if (index < [self count]) {
obj = [self objectAtIndex:index];
}
if ((obj == [NSNull null]) || (obj == nil)) {
return aDefault;
}
}
@catch(NSException* exception) {
NSLog(@"Exception - Name: %@ Reason: %@", [exception name], [exception reason]);
}

return obj;
}

@end