From d04fc289ac2efd0d36f71db2eb198bccee0c34cc Mon Sep 17 00:00:00 2001 From: Braden Shepherdson Date: Wed, 9 Jan 2013 14:22:23 -0500 Subject: [PATCH] Move config.xml parsing into its own Config class Now the parsing happens very early in the bootstrap process, before loadUrl() is called. This enables a future change to put the start page in config.xml instead of hardcoding it. --- bin/templates/project/Activity.java | 2 +- framework/src/org/apache/cordova/Config.java | 196 ++++++++++++++++++ .../apache/cordova/CordovaChromeClient.java | 2 +- .../org/apache/cordova/CordovaWebView.java | 142 +------------ .../apache/cordova/CordovaWebViewClient.java | 3 +- .../src/org/apache/cordova/DroidGap.java | 9 +- .../src/org/apache/cordova/FileTransfer.java | 4 +- .../src/org/apache/cordova/InAppBrowser.java | 4 +- .../cordova/test/actions/basicauth.java | 3 +- 9 files changed, 213 insertions(+), 152 deletions(-) create mode 100644 framework/src/org/apache/cordova/Config.java diff --git a/bin/templates/project/Activity.java b/bin/templates/project/Activity.java index d37247be..4f87f9fd 100644 --- a/bin/templates/project/Activity.java +++ b/bin/templates/project/Activity.java @@ -19,7 +19,6 @@ package __ID__; -import android.app.Activity; import android.os.Bundle; import org.apache.cordova.*; @@ -29,6 +28,7 @@ public class __ACTIVITY__ extends DroidGap public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Config.init(this); super.loadUrl("file:///android_asset/www/index.html"); } } diff --git a/framework/src/org/apache/cordova/Config.java b/framework/src/org/apache/cordova/Config.java new file mode 100644 index 00000000..1713d448 --- /dev/null +++ b/framework/src/org/apache/cordova/Config.java @@ -0,0 +1,196 @@ +/* + 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; + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.cordova.api.LOG; + +import org.xmlpull.v1.XmlPullParserException; + +import android.app.Activity; + +import android.content.res.XmlResourceParser; + +import android.util.Log; + +public class Config { + + public static final String TAG = "Config"; + + private ArrayList whiteList = new ArrayList(); + private HashMap whiteListCache = new HashMap(); + + private static Config self = null; + + public static void init(Activity action) { + if (self == null) { + self = new Config(action); + } + } + + // Intended to be used for testing only; creates an empty configuration. + public static void init() { + if (self == null) { + self = new Config(); + } + } + + // Intended to be used for testing only; creates an empty configuration. + private Config() { + } + + private Config(Activity action) { + if (action == null) { + LOG.i("CordovaLog", "There is no activity. Is this on the lock screen?"); + return; + } + + int id = action.getResources().getIdentifier("config", "xml", action.getPackageName()); + if (id == 0) { + id = action.getResources().getIdentifier("cordova", "xml", action.getPackageName()); + LOG.i("CordovaLog", "config.xml missing, reverting to cordova.xml"); + } + if (id == 0) { + LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); + return; + } + + XmlResourceParser xml = action.getResources().getXml(id); + int eventType = -1; + while (eventType != XmlResourceParser.END_DOCUMENT) { + if (eventType == XmlResourceParser.START_TAG) { + String strNode = xml.getName(); + + if (strNode.equals("access")) { + String origin = xml.getAttributeValue(null, "origin"); + String subdomains = xml.getAttributeValue(null, "subdomains"); + if (origin != null) { + addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); + } + } + else if (strNode.equals("log")) { + String level = xml.getAttributeValue(null, "level"); + LOG.i("CordovaLog", "Found log level %s", level); + if (level != null) { + LOG.setLogLevel(level); + } + } + else if (strNode.equals("preference")) { + String name = xml.getAttributeValue(null, "name"); + String value = xml.getAttributeValue(null, "value"); + + LOG.i("CordovaLog", "Found preference for %s=%s", name, value); + Log.d("CordovaLog", "Found preference for " + name + "=" + value); + + action.getIntent().putExtra(name, value); + } + } + + try { + eventType = xml.next(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * Add entry to approved list of URLs (whitelist) + * + * @param origin URL regular expression to allow + * @param subdomains T=include all subdomains under origin + */ + public static void addWhiteListEntry(String origin, boolean subdomains) { + if (self == null) { + return; + } + + try { + // Unlimited access to network resources + if (origin.compareTo("*") == 0) { + LOG.d(TAG, "Unlimited access to network resources"); + self.whiteList.add(Pattern.compile(".*")); + } else { // specific access + // check if subdomains should be included + // TODO: we should not add more domains if * has already been added + if (subdomains) { + // XXX making it stupid friendly for people who forget to include protocol/SSL + if (origin.startsWith("http")) { + self.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?"))); + } else { + self.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin)); + } + LOG.d(TAG, "Origin to allow with subdomains: %s", origin); + } else { + // XXX making it stupid friendly for people who forget to include protocol/SSL + if (origin.startsWith("http")) { + self.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://"))); + } else { + self.whiteList.add(Pattern.compile("^https?://" + origin)); + } + LOG.d(TAG, "Origin to allow: %s", origin); + } + } + } catch (Exception e) { + LOG.d(TAG, "Failed to add origin %s", origin); + } + } + + /** + * Determine if URL is in approved list of URLs to load. + * + * @param url + * @return + */ + public static boolean isUrlWhiteListed(String url) { + if (self == null) { + return false; + } + + // Check to see if we have matched url previously + if (self.whiteListCache.get(url) != null) { + return true; + } + + // Look for match in white list + Iterator pit = self.whiteList.iterator(); + while (pit.hasNext()) { + Pattern p = pit.next(); + Matcher m = p.matcher(url); + + // If match found, then cache it to speed up subsequent comparisons + if (m.find()) { + self.whiteListCache.put(url, true); + return true; + } + } + return false; + } +} diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java index be7c0c19..9f6ffd9c 100755 --- a/framework/src/org/apache/cordova/CordovaChromeClient.java +++ b/framework/src/org/apache/cordova/CordovaChromeClient.java @@ -197,7 +197,7 @@ public class CordovaChromeClient extends WebChromeClient { // Security check to make sure any requests are coming from the page initially // loaded in webview and not another loaded in an iframe. boolean reqOk = false; - if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || Config.isUrlWhiteListed(url)) { reqOk = true; } diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 336cf521..547a450d 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -19,31 +19,25 @@ package org.apache.cordova; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.Stack; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import org.apache.cordova.Config; import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.CordovaPlugin; import org.apache.cordova.api.LOG; import org.apache.cordova.api.PluginManager; import org.apache.cordova.api.PluginResult; -import org.xmlpull.v1.XmlPullParserException; import android.annotation.SuppressLint; import android.annotation.TargetApi; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.XmlResourceParser; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -66,15 +60,12 @@ public class CordovaWebView extends WebView { public static final String TAG = "CordovaWebView"; - /** The whitelist **/ - private ArrayList whiteList = new ArrayList(); - private HashMap whiteListCache = new HashMap(); private ArrayList keyDownCodes = new ArrayList(); private ArrayList keyUpCodes = new ArrayList(); public PluginManager pluginManager; private boolean paused; - + private BroadcastReceiver receiver; @@ -343,72 +334,6 @@ public class CordovaWebView extends WebView { super.setWebChromeClient(client); } - /** - * Add entry to approved list of URLs (whitelist) - * - * @param origin URL regular expression to allow - * @param subdomains T=include all subdomains under origin - */ - public void addWhiteListEntry(String origin, boolean subdomains) { - try { - // Unlimited access to network resources - if (origin.compareTo("*") == 0) { - LOG.d(TAG, "Unlimited access to network resources"); - this.whiteList.add(Pattern.compile(".*")); - } else { // specific access - // check if subdomains should be included - // TODO: we should not add more domains if * has already been added - if (subdomains) { - // XXX making it stupid friendly for people who forget to include protocol/SSL - if (origin.startsWith("http")) { - this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?"))); - } else { - this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin)); - } - LOG.d(TAG, "Origin to allow with subdomains: %s", origin); - } else { - // XXX making it stupid friendly for people who forget to include protocol/SSL - if (origin.startsWith("http")) { - this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://"))); - } else { - this.whiteList.add(Pattern.compile("^https?://" + origin)); - } - LOG.d(TAG, "Origin to allow: %s", origin); - } - } - } catch (Exception e) { - LOG.d(TAG, "Failed to add origin %s", origin); - } - } - - /** - * Determine if URL is in approved list of URLs to load. - * - * @param url - * @return - */ - public boolean isUrlWhiteListed(String url) { - - // Check to see if we have matched url previously - if (this.whiteListCache.get(url) != null) { - return true; - } - - // Look for match in white list - Iterator pit = this.whiteList.iterator(); - while (pit.hasNext()) { - Pattern p = pit.next(); - Matcher m = p.matcher(url); - - // If match found, then cache it to speed up subsequent comparisons - if (m.find()) { - this.whiteListCache.put(url, true); - return true; - } - } - return false; - } - /** * Load the url into the webview. * @@ -532,8 +457,7 @@ public class CordovaWebView extends WebView { if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) { LOG.d(TAG, ">>> loadUrlNow()"); } - boolean isDocument = this.baseUrl != null && url.indexOf(this.baseUrl) == 0; - if (url.startsWith("file://") || isDocument || url.startsWith("javascript:") || this.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) { super.loadUrl(url); } } @@ -681,7 +605,7 @@ public class CordovaWebView extends WebView { if (!openExternal) { // Make sure url is in whitelist - if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || Config.isUrlWhiteListed(url)) { // TODO: What about params? // Clear out current url from history, since it will be replacing it @@ -718,68 +642,14 @@ public class CordovaWebView extends WebView { } /** - * Load Cordova configuration from res/xml/cordova.xml. + * Check configuration parameters from Config. * Approved list of URLs that can be loaded into DroidGap * * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR) * */ private void loadConfiguration() { - Activity action = this.cordova.getActivity(); - if(action == null) - { - LOG.i("CordovaLog", "There is no activity. Is this on the lock screen?"); - return; - } - int id = getResources().getIdentifier("config", "xml", this.cordova.getActivity().getPackageName()); - if(id == 0) - { - id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName()); - Log.i("CordovaLog", "config.xml missing, reverting to cordova.xml"); - } - if (id == 0) { - LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); - return; - } - XmlResourceParser xml = getResources().getXml(id); - int eventType = -1; - while (eventType != XmlResourceParser.END_DOCUMENT) { - if (eventType == XmlResourceParser.START_TAG) { - String strNode = xml.getName(); - if (strNode.equals("access")) { - String origin = xml.getAttributeValue(null, "origin"); - String subdomains = xml.getAttributeValue(null, "subdomains"); - if (origin != null) { - this.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); - } - } - else if (strNode.equals("log")) { - String level = xml.getAttributeValue(null, "level"); - LOG.i("CordovaLog", "Found log level %s", level); - if (level != null) { - LOG.setLogLevel(level); - } - } - else if (strNode.equals("preference")) { - String name = xml.getAttributeValue(null, "name"); - String value = xml.getAttributeValue(null, "value"); - - LOG.i("CordovaLog", "Found preference for %s=%s", name, value); - Log.d("CordovaLog", "Found preference for " + name + "=" + value); - - // Save preferences in Intent - this.cordova.getActivity().getIntent().putExtra(name, value); - } - } - try { - eventType = xml.next(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - + // Config has already been loaded, and it stores these preferences on the Intent. if("false".equals(this.getProperty("useBrowserHistory", "true"))) { //Switch back to the old browser history and state the six month policy diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 63ab3255..7a7e6ed9 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -21,7 +21,6 @@ package org.apache.cordova; import java.util.Hashtable; import org.apache.cordova.api.CordovaInterface; -import org.apache.cordova.api.PluginResult; import org.apache.cordova.api.LOG; import org.json.JSONException; @@ -193,7 +192,7 @@ public class CordovaWebViewClient extends WebViewClient { // If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity. // Our app continues to run. When BACK is pressed, our app is redisplayed. - if (url.startsWith("file://") || url.startsWith("data:") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.startsWith("data:") || url.indexOf(this.appView.baseUrl) == 0 || Config.isUrlWhiteListed(url)) { //This will fix iFrames if (appView.useBrowserHistory || url.startsWith("data:")) return false; diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 6f16385f..175fe139 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -48,7 +48,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; @@ -853,7 +852,7 @@ public class DroidGap extends Activity implements CordovaInterface { // If errorUrl specified, then load it final String errorUrl = me.getStringProperty("errorUrl", null); - if ((errorUrl != null) && (errorUrl.startsWith("file://") || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { + if ((errorUrl != null) && (errorUrl.startsWith("file://") || Config.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { // Load URL on UI thread me.runOnUiThread(new Runnable() { @@ -921,11 +920,7 @@ public class DroidGap extends Activity implements CordovaInterface { * @return */ public boolean isUrlWhiteListed(String url) { - // Check to see if we have matched url previously - if (this.appView != null) { - return this.appView.isUrlWhiteListed(url); - } - return false; + return Config.isUrlWhiteListed(url); } /* diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java index 9bec20c9..d3e304f1 100644 --- a/framework/src/org/apache/cordova/FileTransfer.java +++ b/framework/src/org/apache/cordova/FileTransfer.java @@ -602,8 +602,8 @@ public class FileTransfer extends CordovaPlugin { return; } final boolean useHttps = url.getProtocol().toLowerCase().equals("https"); - - if (!webView.isUrlWhiteListed(source)) { + + if (!Config.isUrlWhiteListed(source)) { Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'"); JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, 401); callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error)); diff --git a/framework/src/org/apache/cordova/InAppBrowser.java b/framework/src/org/apache/cordova/InAppBrowser.java index 886a95b0..0d834328 100644 --- a/framework/src/org/apache/cordova/InAppBrowser.java +++ b/framework/src/org/apache/cordova/InAppBrowser.java @@ -105,7 +105,7 @@ public class InAppBrowser extends CordovaPlugin { Log.d(LOG_TAG, "in self"); // load in webview if (url.startsWith("file://") || url.startsWith("javascript:") - || this.webView.isUrlWhiteListed(url)) { + || Config.isUrlWhiteListed(url)) { this.webView.loadUrl(url); } // load in InAppBrowser @@ -531,4 +531,4 @@ public class InAppBrowser extends CordovaPlugin { } } } -} \ No newline at end of file +} diff --git a/test/src/org/apache/cordova/test/actions/basicauth.java b/test/src/org/apache/cordova/test/actions/basicauth.java index bd32d58f..ad739c9e 100755 --- a/test/src/org/apache/cordova/test/actions/basicauth.java +++ b/test/src/org/apache/cordova/test/actions/basicauth.java @@ -34,7 +34,8 @@ public class basicauth extends DroidGap { super.setAuthenticationToken(token, "browserspy.dk:80", "BrowserSpy.dk - HTTP Password Test"); // Add web site to whitelist - super.appView.addWhiteListEntry("http://browserspy.dk*", true); + Config.init(); + Config.addWhiteListEntry("http://browserspy.dk*", true); // Load test super.loadUrl("file:///android_asset/www/basicauth/index.html");