diff options
| author | pliablepixels <pliablepixels@gmail.com> | 2016-02-02 11:40:05 -0500 |
|---|---|---|
| committer | pliablepixels <pliablepixels@gmail.com> | 2016-02-02 11:40:05 -0500 |
| commit | cdd79ac61beb1db8c7135252fb57e7e1a4e78678 (patch) | |
| tree | afb5351e0c90c9d8eb5f99442de10782e6365370 /www/external | |
| parent | 266189e452ca2591ef06b2038e6890f3353e703a (diff) | |
#157 - SSL certificate install should no longer be needed
Former-commit-id: 32b58f287cd4088375f2296c405ce121b7459570
Diffstat (limited to 'www/external')
| -rw-r--r-- | www/external/AppDelegate.m | 160 | ||||
| -rwxr-xr-x | www/external/SystemWebViewClient.java | 378 |
2 files changed, 538 insertions, 0 deletions
diff --git a/www/external/AppDelegate.m b/www/external/AppDelegate.m new file mode 100644 index 00000000..0b13600a --- /dev/null +++ b/www/external/AppDelegate.m @@ -0,0 +1,160 @@ +/* + 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. + */ + +// +// AppDelegate.m +// zmNinja +// +// Created by ___FULLUSERNAME___ on ___DATE___. +// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. +// + +#import "AppDelegate.h" +#import "MainViewController.h" + +#import <Cordova/CDVPlugin.h> + +@implementation AppDelegate + +@synthesize window, viewController; + +- (id)init +{ + /** If you need to do any extra app-specific initialization, you can do it here + * -jm + **/ + NSHTTPCookieStorage* cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + + [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; + + int cacheSizeMemory = 8 * 1024 * 1024; // 8MB + int cacheSizeDisk = 32 * 1024 * 1024; // 32MB +#if __has_feature(objc_arc) + NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"]; +#else + NSURLCache* sharedCache = [[[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"] autorelease]; +#endif + [NSURLCache setSharedURLCache:sharedCache]; + + self = [super init]; + return self; +} + +#pragma mark UIApplicationDelegate implementation + +/** + * This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up) + */ +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions +{ + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + +#if __has_feature(objc_arc) + self.window = [[UIWindow alloc] initWithFrame:screenBounds]; +#else + self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease]; +#endif + self.window.autoresizesSubviews = YES; + +#if __has_feature(objc_arc) + self.viewController = [[MainViewController alloc] init]; +#else + self.viewController = [[[MainViewController alloc] init] autorelease]; +#endif + + // Set your app's start page by setting the <content src='foo.html' /> tag in config.xml. + // If necessary, uncomment the line below to override it. + // self.viewController.startPage = @"index.html"; + + // NOTE: To customize the view's frame size (which defaults to full screen), override + // [self.viewController viewWillAppear:] in your view controller. + + self.window.rootViewController = self.viewController; + [self.window makeKeyAndVisible]; + + return YES; +} + +// this happens while we are running ( in the background, or from within our own app ) +// only valid if zmNinja-Info.plist specifies a protocol to handle +- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation +{ + if (!url) { + return NO; + } + + // all plugins will get the notification, and their handlers will be called + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + + return YES; +} + +// repost all remote and local notification using the default NSNotificationCenter so multiple plugins may respond +- (void) application:(UIApplication*)application + didReceiveLocalNotification:(UILocalNotification*)notification +{ + // re-post ( broadcast ) + [[NSNotificationCenter defaultCenter] postNotificationName:CDVLocalNotification object:notification]; +} + +#ifndef DISABLE_PUSH_NOTIFICATIONS + + - (void) application:(UIApplication*)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken + { + // re-post ( broadcast ) + NSString* token = [[[[deviceToken description] + stringByReplacingOccurrencesOfString:@"<" withString:@""] + stringByReplacingOccurrencesOfString:@">" withString:@""] + stringByReplacingOccurrencesOfString:@" " withString:@""]; + + [[NSNotificationCenter defaultCenter] postNotificationName:CDVRemoteNotification object:token]; + } + + - (void) application:(UIApplication*)application + didFailToRegisterForRemoteNotificationsWithError:(NSError*)error + { + // re-post ( broadcast ) + [[NSNotificationCenter defaultCenter] postNotificationName:CDVRemoteNotificationError object:error]; + } +#endif + +- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window +{ + // iPhone doesn't support upside down by default, while the iPad does. Override to allow all orientations always, and let the root view controller decide what's allowed (the supported orientations mask gets intersected). + NSUInteger supportedInterfaceOrientations = (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationLandscapeLeft) | (1 << UIInterfaceOrientationLandscapeRight) | (1 << UIInterfaceOrientationPortraitUpsideDown); + + return supportedInterfaceOrientations; +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application +{ + [[NSURLCache sharedURLCache] removeAllCachedResponses]; +} +@end + +// credit: http://ivancevich.me/articles/ignoring-invalid-ssl-certificates-on-cordova-android-ios/ + +@implementation NSURLRequest(DataController) ++ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host +{ + return YES; +} +@end + diff --git a/www/external/SystemWebViewClient.java b/www/external/SystemWebViewClient.java new file mode 100755 index 00000000..86b3f0ce --- /dev/null +++ b/www/external/SystemWebViewClient.java @@ -0,0 +1,378 @@ +/* + 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. +*/ +package org.apache.cordova.engine; + +import android.annotation.TargetApi; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.Bitmap; +import android.net.Uri; +import android.net.http.SslError; +import android.os.Build; +import android.webkit.ClientCertRequest; +import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import org.apache.cordova.AuthenticationToken; +import org.apache.cordova.CordovaClientCertRequest; +import org.apache.cordova.CordovaHttpAuthHandler; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginManager; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Hashtable; + + +/** + * This class is the WebViewClient that implements callbacks for our web view. + * The kind of callbacks that happen here are regarding the rendering of the + * document instead of the chrome surrounding it, such as onPageStarted(), + * shouldOverrideUrlLoading(), etc. Related to but different than + * CordovaChromeClient. + */ +public class SystemWebViewClient extends WebViewClient { + + private static final String TAG = "SystemWebViewClient"; + protected final SystemWebViewEngine parentEngine; + private boolean doClearHistory = false; + boolean isCurrentlyLoading; + + /** The authorization tokens. */ + private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>(); + + public SystemWebViewClient(SystemWebViewEngine parentEngine) { + this.parentEngine = parentEngine; + } + + /** + * Give the host application a chance to take over the control when a new url + * is about to be loaded in the current WebView. + * + * @param view The WebView that is initiating the callback. + * @param url The url to be loaded. + * @return true to override, false for default behavior + */ + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return parentEngine.client.onNavigationAttempt(url); + } + + /** + * On received http auth request. + * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination + */ + @Override + public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { + + // Get the authentication token (if specified) + AuthenticationToken token = this.getAuthenticationToken(host, realm); + if (token != null) { + handler.proceed(token.getUserName(), token.getPassword()); + return; + } + + // Check if there is some plugin which can resolve this auth challenge + PluginManager pluginManager = this.parentEngine.pluginManager; + if (pluginManager != null && pluginManager.onReceivedHttpAuthRequest(null, new CordovaHttpAuthHandler(handler), host, realm)) { + parentEngine.client.clearLoadTimeoutTimer(); + return; + } + + // By default handle 401 like we'd normally do! + super.onReceivedHttpAuthRequest(view, handler, host, realm); + } + + /** + * On received client cert request. + * The method forwards the request to any running plugins before using the default implementation. + * + * @param view + * @param request + */ + @Override + @TargetApi(21) + public void onReceivedClientCertRequest (WebView view, ClientCertRequest request) + { + + // Check if there is some plugin which can resolve this certificate request + PluginManager pluginManager = this.parentEngine.pluginManager; + if (pluginManager != null && pluginManager.onReceivedClientCertRequest(null, new CordovaClientCertRequest(request))) { + parentEngine.client.clearLoadTimeoutTimer(); + return; + } + + // By default pass to WebViewClient + super.onReceivedClientCertRequest(view, request); + } + + /** + * Notify the host application that a page has started loading. + * This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted + * one time for the main frame. This also means that onPageStarted will not be called when the contents of an + * embedded frame changes, i.e. clicking a link whose target is an iframe. + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + isCurrentlyLoading = true; + // Flush stale messages & reset plugins. + parentEngine.bridge.reset(); + parentEngine.client.onPageStarted(url); + } + + /** + * Notify the host application that a page has finished loading. + * This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet. + * + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + // Ignore excessive calls, if url is not about:blank (CB-8317). + if (!isCurrentlyLoading && !url.startsWith("about:")) { + return; + } + isCurrentlyLoading = false; + + /** + * Because of a timing issue we need to clear this history in onPageFinished as well as + * onPageStarted. However we only want to do this if the doClearHistory boolean is set to + * true. You see when you load a url with a # in it which is common in jQuery applications + * onPageStared is not called. Clearing the history at that point would break jQuery apps. + */ + if (this.doClearHistory) { + view.clearHistory(); + this.doClearHistory = false; + } + parentEngine.client.onPageFinishedLoading(url); + + } + + /** + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * The errorCode parameter corresponds to one of the ERROR_* constants. + * + * @param view The WebView that is initiating the callback. + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + */ + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + // Ignore error due to stopLoading(). + if (!isCurrentlyLoading) { + return; + } + LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl); + + // If this is a "Protocol Not Supported" error, then revert to the previous + // page. If there was no previous page, then punt. The application's config + // is likely incorrect (start page set to sms: or something like that) + if (errorCode == WebViewClient.ERROR_UNSUPPORTED_SCHEME) { + parentEngine.client.clearLoadTimeoutTimer(); + + if (view.canGoBack()) { + view.goBack(); + return; + } else { + super.onReceivedError(view, errorCode, description, failingUrl); + } + } + parentEngine.client.onReceivedError(errorCode, description, failingUrl); + } + + /** + * Notify the host application that an SSL error occurred while loading a resource. + * The host application must call either handler.cancel() or handler.proceed(). + * Note that the decision may be retained for use in response to future SSL errors. + * The default behavior is to cancel the load. + * + * @param view The WebView that is initiating the callback. + * @param handler An SslErrorHandler object that will handle the user's response. + * @param error The SSL error object. + */ + @TargetApi(8) + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + + final String packageName = parentEngine.cordova.getActivity().getPackageName(); + final PackageManager pm = parentEngine.cordova.getActivity().getPackageManager(); + + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + // debug = true + handler.proceed(); + return; + } else { + // debug = false + // credit http://ivancevich.me/articles/ignoring-invalid-ssl-certificates-on-cordova-android-ios/ + + // super.onReceivedSslError(view, handler, error); + handler.proceed(); + return; + } + } catch (NameNotFoundException e) { + // When it doubt, lock it out! + super.onReceivedSslError(view, handler, error); + } + } + + + /** + * Sets the authentication token. + * + * @param authenticationToken + * @param host + * @param realm + */ + public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) { + if (host == null) { + host = ""; + } + if (realm == null) { + realm = ""; + } + this.authenticationTokens.put(host.concat(realm), authenticationToken); + } + + /** + * Removes the authentication token. + * + * @param host + * @param realm + * + * @return the authentication token or null if did not exist + */ + public AuthenticationToken removeAuthenticationToken(String host, String realm) { + return this.authenticationTokens.remove(host.concat(realm)); + } + + /** + * Gets the authentication token. + * + * In order it tries: + * 1- host + realm + * 2- host + * 3- realm + * 4- no host, no realm + * + * @param host + * @param realm + * + * @return the authentication token + */ + public AuthenticationToken getAuthenticationToken(String host, String realm) { + AuthenticationToken token = null; + token = this.authenticationTokens.get(host.concat(realm)); + + if (token == null) { + // try with just the host + token = this.authenticationTokens.get(host); + + // Try the realm + if (token == null) { + token = this.authenticationTokens.get(realm); + } + + // if no host found, just query for default + if (token == null) { + token = this.authenticationTokens.get(""); + } + } + + return token; + } + + /** + * Clear all authentication tokens. + */ + public void clearAuthenticationTokens() { + this.authenticationTokens.clear(); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + try { + // Check the against the whitelist and lock out access to the WebView directory + // Changing this will cause problems for your application + if (!parentEngine.pluginManager.shouldAllowRequest(url)) { + LOG.w(TAG, "URL blocked by whitelist: " + url); + // Results in a 404. + return new WebResourceResponse("text/plain", "UTF-8", null); + } + + CordovaResourceApi resourceApi = parentEngine.resourceApi; + Uri origUri = Uri.parse(url); + // Allow plugins to intercept WebView requests. + Uri remappedUri = resourceApi.remapUri(origUri); + + if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri) || needsKitKatContentUrlFix(origUri)) { + CordovaResourceApi.OpenForReadResult result = resourceApi.openForRead(remappedUri, true); + return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream); + } + // If we don't need to special-case the request, let the browser load it. + return null; + } catch (IOException e) { + if (!(e instanceof FileNotFoundException)) { + LOG.e(TAG, "Error occurred while loading a file (returning a 404).", e); + } + // Results in a 404. + return new WebResourceResponse("text/plain", "UTF-8", null); + } + } + + private static boolean needsKitKatContentUrlFix(Uri uri) { + return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme()); + } + + private static boolean needsSpecialsInAssetUrlFix(Uri uri) { + if (CordovaResourceApi.getUriType(uri) != CordovaResourceApi.URI_TYPE_ASSET) { + return false; + } + if (uri.getQuery() != null || uri.getFragment() != null) { + return true; + } + + if (!uri.toString().contains("%")) { + return false; + } + + switch(android.os.Build.VERSION.SDK_INT){ + case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH: + case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1: + return true; + } + return false; + } +} |
