/**
   * 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 errorCode The error code corresponding to an ERROR_* value.
   * @param description A String describing the error.
   * @param failingUrl The url that failed to load.
   */
  public void onReceivedError(
      final int errorCode, final String description, final String failingUrl) {
    final CordovaActivity me = this;

    // If errorUrl specified, then load it
    final String errorUrl = preferences.getString("errorUrl", null);
    if ((errorUrl != null)
        && (errorUrl.startsWith("file://") || whitelist.isUrlWhiteListed(errorUrl))
        && (!failingUrl.equals(errorUrl))) {

      // Load URL on UI thread
      me.runOnUiThread(
          new Runnable() {
            public void run() {
              // Stop "app loading" spinner if showing
              me.spinnerStop();
              me.appView.showWebPage(errorUrl, false, true, null);
            }
          });
    }
    // If not, then display error dialog
    else {
      final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
      me.runOnUiThread(
          new Runnable() {
            public void run() {
              if (exit) {
                me.appView.setVisibility(View.GONE);
                me.displayError(
                    "Application Error", description + " (" + failingUrl + ")", "OK", exit);
              }
            }
          });
    }
  }
Ejemplo n.º 2
0
 /** Load URL in webview. */
 private void loadUrlNow(String url) {
   if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
     LOG.d(TAG, ">>> loadUrlNow()");
   }
   if (url.startsWith("file://")
       || url.startsWith("javascript:")
       || url.startsWith("about:")
       || internalWhitelist.isUrlWhiteListed(url)) {
     super.loadUrl(url);
   }
 }
Ejemplo n.º 3
0
  /**
   * Load the specified URL in the Cordova webview or a new browser instance.
   *
   * <p>NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
   *
   * @param url The url to load.
   * @param openExternal Load url in browser instead of Cordova webview.
   * @param clearHistory Clear the history stack, so new page becomes top of history
   * @param params Parameters for new app
   */
  public void showWebPage(
      String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
    LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory);

    // If clearing history
    if (clearHistory) {
      this.clearHistory();
    }

    // If loading into our webview
    if (!openExternal) {

      // Make sure url is in whitelist
      if (url.startsWith("file://") || internalWhitelist.isUrlWhiteListed(url)) {
        // TODO: What about params?
        // Load new URL
        loadUrlIntoView(url, true);
        return;
      }
      // Load in default viewer if not
      LOG.w(
          TAG,
          "showWebPage: Cannot load URL into webview since it is not in white list.  Loading into browser instead. (URL="
              + url
              + ")");
    }
    try {
      // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent".
      // Adding the MIME type to http: URLs causes them to not be handled by the downloader.
      Intent intent = new Intent(Intent.ACTION_VIEW);
      Uri uri = Uri.parse(url);
      if ("file".equals(uri.getScheme())) {
        intent.setDataAndType(uri, resourceApi.getMimeType(uri));
      } else {
        intent.setData(uri);
      }
      cordova.getActivity().startActivity(intent);
    } catch (android.content.ActivityNotFoundException e) {
      LOG.e(TAG, "Error loading url " + url, e);
    }
  }
 /** Determine if URL is in approved list of URLs to load. */
 @Deprecated // Use whitelist object directly.
 public boolean isUrlWhiteListed(String url) {
   return whitelist.isUrlWhiteListed(url);
 }
  /**
   * Downloads a file form a given URL and saves it to the specified directory.
   *
   * @param source URL of the server to receive the file
   * @param target Full path of the file on the file system
   */
  private void download(
      final String source, final String target, JSONArray args, CallbackContext callbackContext)
      throws JSONException {
    Log.d(LOG_TAG, "download " + source + " to " + target);

    final CordovaResourceApi resourceApi = webView.getResourceApi();

    final boolean trustEveryone = args.optBoolean(2);
    final String objectId = args.getString(3);
    final JSONObject options = args.getJSONObject(4);
    final JSONObject headers = options.getJSONObject("headers");

    final Uri sourceUri = resourceApi.remapUri(Uri.parse(source));
    // Accept a path or a URI for the source.
    Uri tmpTarget = Uri.parse(target);
    final Uri targetUri =
        resourceApi.remapUri(
            tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target)));

    int uriType = CordovaResourceApi.getUriType(sourceUri);
    final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
    final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP;
    if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) {
      JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0, null);
      Log.e(LOG_TAG, "Unsupported URI: " + sourceUri);
      callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
      return;
    }

    /* This code exists for compatibility between 3.x and 4.x versions of Cordova.
     * Previously the CordovaWebView class had a method, getWhitelist, which would
     * return a Whitelist object. Since the fixed whitelist is removed in Cordova 4.x,
     * the correct call now is to shouldAllowRequest from the plugin manager.
     */
    Boolean shouldAllowRequest = null;
    if (isLocalTransfer) {
      shouldAllowRequest = true;
    }
    if (shouldAllowRequest == null) {
      try {
        Method gwl = webView.getClass().getMethod("getWhitelist");
        Whitelist whitelist = (Whitelist) gwl.invoke(webView);
        shouldAllowRequest = whitelist.isUrlWhiteListed(source);
      } catch (NoSuchMethodException e) {
      } catch (IllegalAccessException e) {
      } catch (InvocationTargetException e) {
      }
    }
    if (shouldAllowRequest == null) {
      try {
        Method gpm = webView.getClass().getMethod("getPluginManager");
        PluginManager pm = (PluginManager) gpm.invoke(webView);
        Method san = pm.getClass().getMethod("shouldAllowRequest", String.class);
        shouldAllowRequest = (Boolean) san.invoke(pm, source);
      } catch (NoSuchMethodException e) {
      } catch (IllegalAccessException e) {
      } catch (InvocationTargetException e) {
      }
    }

    if (!Boolean.TRUE.equals(shouldAllowRequest)) {
      Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
      JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401, null);
      callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
      return;
    }

    final RequestContext context = new RequestContext(source, target, callbackContext);
    synchronized (activeRequests) {
      activeRequests.put(objectId, context);
    }

    cordova
        .getThreadPool()
        .execute(
            new Runnable() {
              public void run() {
                if (context.aborted) {
                  return;
                }
                HttpURLConnection connection = null;
                HostnameVerifier oldHostnameVerifier = null;
                SSLSocketFactory oldSocketFactory = null;
                File file = null;
                Long downloaded = 0L;
                PluginResult result = null;
                TrackingInputStream inputStream = null;
                boolean cached = false;

                OutputStream outputStream = null;
                try {
                  OpenForReadResult readResult = null;

                  file = resourceApi.mapUriToFile(targetUri);
                  context.targetFile = file;

                  Log.d(LOG_TAG, "Download file:" + sourceUri);

                  FileProgressResult progress = new FileProgressResult();

                  if (isLocalTransfer) {
                    readResult = resourceApi.openForRead(sourceUri);
                    if (readResult.length != -1) {
                      progress.setLengthComputable(true);
                      progress.setTotal(readResult.length);
                    } else {
                      progress.setLengthComputable(false);
                      progress.setTotal(-1);
                    }
                    inputStream = new SimpleTrackingInputStream(readResult.inputStream);
                  } else {
                    // connect to server
                    // Open a HTTP connection to the URL based on protocol
                    connection = resourceApi.createHttpConnection(sourceUri);
                    if (useHttps && trustEveryone) {
                      // Setup the HTTPS connection class to trust everyone
                      HttpsURLConnection https = (HttpsURLConnection) connection;
                      oldSocketFactory = trustAllHosts(https);
                      // Save the current hostnameVerifier
                      oldHostnameVerifier = https.getHostnameVerifier();
                      // Setup the connection not to verify hostnames
                      https.setHostnameVerifier(DO_NOT_VERIFY);
                    }

                    connection.setRequestMethod("GET");

                    // TODO: Make OkHttp use this CookieManager by default.
                    String cookie = getCookies(sourceUri.toString());

                    if (cookie != null) {
                      connection.setRequestProperty("cookie", cookie);
                    }

                    // This must be explicitly set for gzip progress tracking to work.
                    connection.setRequestProperty("Accept-Encoding", "gzip");

                    // Handle the other headers
                    if (headers != null) {
                      addHeadersToRequest(connection, headers);
                    }

                    if (options.getBoolean("resume") && file.exists()) {
                      downloaded = file.length();
                      connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
                    }

                    connection.connect();
                    if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
                      cached = true;
                      connection.disconnect();
                      Log.d(LOG_TAG, "Resource not modified: " + source);
                      JSONObject error =
                          createFileTransferError(
                              NOT_MODIFIED_ERR, source, target, connection, null);
                      result = new PluginResult(PluginResult.Status.ERROR, error);
                    } else {
                      if (connection.getContentEncoding() == null
                          || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
                        // Only trust content-length header if we understand
                        // the encoding -- identity or gzip
                        if (connection.getContentLength() != -1) {
                          progress.setLengthComputable(true);
                          progress.setTotal(connection.getContentLength() + downloaded);
                        } else {
                          progress.setLengthComputable(false);
                          progress.setTotal(-1);
                        }
                      }
                      inputStream = getInputStream(connection);
                    }
                  }

                  if (!cached) {
                    try {
                      synchronized (context) {
                        if (context.aborted) {
                          return;
                        }
                        context.connection = connection;
                      }

                      // write bytes to file
                      byte[] buffer = new byte[MAX_BUFFER_SIZE];
                      int bytesRead = 0;
                      boolean append = downloaded > 0;
                      outputStream = resourceApi.openOutputStream(targetUri, append);
                      while ((bytesRead = inputStream.read(buffer)) > 0) {
                        outputStream.write(buffer, 0, bytesRead);
                        // Send a progress event.
                        progress.setLoaded(inputStream.getTotalRawBytesRead() + downloaded);
                        PluginResult progressResult =
                            new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
                        progressResult.setKeepCallback(true);
                        context.sendPluginResult(progressResult);
                      }
                    } catch (IOException e) {
                      outputStream.flush();
                    } finally {
                      synchronized (context) {
                        context.connection = null;
                      }
                      safeClose(inputStream);
                      safeClose(outputStream);
                    }

                    Log.d(LOG_TAG, "Saved file: " + target);

                    // create FileEntry object
                    Class webViewClass = webView.getClass();
                    PluginManager pm = null;
                    try {
                      Method gpm = webViewClass.getMethod("getPluginManager");
                      pm = (PluginManager) gpm.invoke(webView);
                    } catch (NoSuchMethodException e) {
                    } catch (IllegalAccessException e) {
                    } catch (InvocationTargetException e) {
                    }
                    if (pm == null) {
                      try {
                        Field pmf = webViewClass.getField("pluginManager");
                        pm = (PluginManager) pmf.get(webView);
                      } catch (NoSuchFieldException e) {
                      } catch (IllegalAccessException e) {
                      }
                    }
                    file = resourceApi.mapUriToFile(targetUri);
                    context.targetFile = file;
                    FileUtils filePlugin = (FileUtils) pm.getPlugin("File");
                    if (filePlugin != null) {
                      JSONObject fileEntry = filePlugin.getEntryForFile(file);
                      if (fileEntry != null) {
                        result = new PluginResult(PluginResult.Status.OK, fileEntry);
                      } else {
                        JSONObject error =
                            createFileTransferError(
                                CONNECTION_ERR, source, target, connection, null);
                        Log.e(LOG_TAG, "File plugin cannot represent download path");
                        result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
                      }
                    } else {
                      Log.e(LOG_TAG, "File plugin not found; cannot save downloaded file");
                      result =
                          new PluginResult(
                              PluginResult.Status.ERROR,
                              "File plugin not found; cannot save downloaded file");
                    }
                  }
                } catch (FileNotFoundException e) {
                  JSONObject error =
                      createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection, e);
                  Log.e(LOG_TAG, error.toString(), e);
                  result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
                } catch (IOException e) {
                  JSONObject error =
                      createFileTransferError(CONNECTION_ERR, source, target, connection, e);
                  Log.e(LOG_TAG, error.toString(), e);
                  result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
                } catch (JSONException e) {
                  Log.e(LOG_TAG, e.getMessage(), e);
                  result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
                } catch (Throwable e) {
                  JSONObject error =
                      createFileTransferError(CONNECTION_ERR, source, target, connection, e);
                  Log.e(LOG_TAG, error.toString(), e);
                  result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
                } finally {
                  synchronized (activeRequests) {
                    activeRequests.remove(objectId);
                  }

                  if (connection != null) {
                    // Revert back to the proper verifier and socket factories
                    if (trustEveryone && useHttps) {
                      HttpsURLConnection https = (HttpsURLConnection) connection;
                      https.setHostnameVerifier(oldHostnameVerifier);
                      https.setSSLSocketFactory(oldSocketFactory);
                    }
                  }

                  if (result == null) {
                    result =
                        new PluginResult(
                            PluginResult.Status.ERROR,
                            createFileTransferError(
                                CONNECTION_ERR, source, target, connection, null));
                  }
                  // Remove incomplete download.
                  if (!cached
                      && result.getStatus() != PluginResult.Status.OK.ordinal()
                      && file != null) {
                    file.delete();
                  }
                  context.sendPluginResult(result);
                }
              }
            });
  }