From 2ae223af4b71082fbb353d304b614283de2ba08b Mon Sep 17 00:00:00 2001 From: Erisu Date: Fri, 4 Feb 2022 23:49:41 +0900 Subject: [PATCH 01/10] feat: add WebAssetLoader proxy handler for cdvfile --- plugin.xml | 2 + src/android/AssetFilesystem.java | 5 +- src/android/ContentFilesystem.java | 6 ++- src/android/FileUtils.java | 77 +++++++++++++++++++++++++++--- src/android/Filesystem.java | 32 +++++++++++-- src/android/LocalFilesystem.java | 6 ++- www/DirectoryEntry.js | 2 +- www/Entry.js | 11 ++++- www/FileEntry.js | 7 ++- 9 files changed, 127 insertions(+), 21 deletions(-) diff --git a/plugin.xml b/plugin.xml index 64331dc59..edb7536ac 100644 --- a/plugin.xml +++ b/plugin.xml @@ -153,6 +153,8 @@ to config.xml in order for the application to find previously stored files. + + diff --git a/src/android/AssetFilesystem.java b/src/android/AssetFilesystem.java index b035c40e6..72dbfd24b 100644 --- a/src/android/AssetFilesystem.java +++ b/src/android/AssetFilesystem.java @@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one import android.content.res.AssetManager; import android.net.Uri; +import org.apache.cordova.CordovaPreferences; import org.apache.cordova.CordovaResourceApi; import org.apache.cordova.LOG; import org.json.JSONArray; @@ -133,8 +134,8 @@ private long getAssetSize(String assetPath) throws FileNotFoundException { } } - public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi) { - super(Uri.parse("file:///android_asset/"), "assets", resourceApi); + public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi, CordovaPreferences preferences) { + super(Uri.parse("file:///android_asset/"), "assets", resourceApi, preferences); this.assetManager = assetManager; } diff --git a/src/android/ContentFilesystem.java b/src/android/ContentFilesystem.java index 6b983c089..cdb5e4866 100644 --- a/src/android/ContentFilesystem.java +++ b/src/android/ContentFilesystem.java @@ -28,6 +28,8 @@ Licensed to the Apache Software Foundation (ASF) under one import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; + +import org.apache.cordova.CordovaPreferences; import org.apache.cordova.CordovaResourceApi; import org.json.JSONException; import org.json.JSONObject; @@ -36,8 +38,8 @@ public class ContentFilesystem extends Filesystem { private final Context context; - public ContentFilesystem(Context context, CordovaResourceApi resourceApi) { - super(Uri.parse("content://"), "content", resourceApi); + public ContentFilesystem(Context context, CordovaResourceApi resourceApi, CordovaPreferences preferences) { + super(Uri.parse("content://"), "content", resourceApi, preferences); this.context = context; } diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index 695af7a59..1d2ee85e5 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -20,16 +20,23 @@ Licensed to the Apache Software Foundation (ASF) under one import android.Manifest; import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.util.Base64; +import android.util.Log; +import android.webkit.MimeTypeMap; +import android.webkit.WebResourceResponse; + +import androidx.webkit.WebViewAssetLoader; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaPluginPathHandler; import org.apache.cordova.CordovaWebView; import org.apache.cordova.LOG; import org.apache.cordova.PermissionHelper; @@ -39,12 +46,16 @@ Licensed to the Apache Software Foundation (ASF) under one import org.json.JSONException; import org.json.JSONObject; +import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.MalformedURLException; +import java.net.URL; import java.security.Permission; import java.util.ArrayList; import java.util.HashMap; @@ -87,8 +98,6 @@ public class FileUtils extends CordovaPlugin { private PendingRequests pendingRequests; - - /* * We need both read and write when accessing the storage, I think. */ @@ -136,7 +145,7 @@ protected void registerExtraFileSystems(String[] filesystems, HashMap { + String targetFileSystem = null; + + // currently only supports persistent & temporary + if (path.startsWith("__cdvfile_persistent__")) { + targetFileSystem = "persistent"; + } else if (path.startsWith("__cdvfile_temporary__")) { + targetFileSystem = "temporary"; + } + + if (targetFileSystem != null) { + // Loop the registered file systems to find the target. + for (Filesystem fileSystem : filesystems) { + + /* + * When target is discovered: + * 1. Transform the url path to the native path + * 2. Load the file contents + * 3. Get the file mime type + * 4. Return the file & mime information back we Web Resources + */ + if (fileSystem.name.equals(targetFileSystem)) { + // replace __cdvfile_persistent__ with native path "/data/user/0/com.example.file/files/files/" + String fileSystemNativeUri = fileSystem.rootUri.toString().replace("file://", ""); + String persistentFileTarget = path.replace("__cdvfile_persistent__/", fileSystemNativeUri); + + File file = new File(persistentFileTarget); + + try { + InputStream in = new FileInputStream(file); + String mimeType = getMimeType(Uri.parse(file.toString())); + return new WebResourceResponse(mimeType, null, in); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, e.getMessage()); + } + } + } + } + + return null; + }; + + Log.d(LOG_TAG, "Added CDVFile Proxy"); + return new CordovaPluginPathHandler(pathHandler); + } } diff --git a/src/android/Filesystem.java b/src/android/Filesystem.java index c69d3bdd0..3b7e0957d 100644 --- a/src/android/Filesystem.java +++ b/src/android/Filesystem.java @@ -19,6 +19,7 @@ Licensed to the Apache Software Foundation (ASF) under one package org.apache.cordova.file; import android.net.Uri; +import android.util.Log; import java.io.File; import java.io.FileNotFoundException; @@ -29,6 +30,7 @@ Licensed to the Apache Software Foundation (ASF) under one import java.util.ArrayList; import java.util.Arrays; +import org.apache.cordova.CordovaPreferences; import org.apache.cordova.CordovaResourceApi; import org.json.JSONArray; import org.json.JSONException; @@ -38,20 +40,25 @@ public abstract class Filesystem { protected final Uri rootUri; protected final CordovaResourceApi resourceApi; + protected final CordovaPreferences preferences; public final String name; private JSONObject rootEntry; - public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) { + private static String SCHEME_HTTPS = "https"; + private static String DEFAULT_HOSTNAME = "localhost"; + + public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi, CordovaPreferences preferences) { this.rootUri = rootUri; this.name = name; this.resourceApi = resourceApi; + this.preferences = preferences; } public interface ReadFileCallback { public void handleData(InputStream inputStream, String contentType) throws IOException; } - public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL) { + public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL, CordovaPreferences preferences) { try { String path = inputURL.path; int end = path.endsWith("/") ? 1 : 0; @@ -74,6 +81,23 @@ public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri native nativeUrlStr += "/"; } entry.put("nativeURL", nativeUrlStr); + + String cdvURL = ""; + + if (!preferences.getBoolean("AndroidInsecureFileModeEnabled", false)) { + String scheme = preferences.getString("scheme", SCHEME_HTTPS).toLowerCase(); + String hostname = preferences.getString("hostname", DEFAULT_HOSTNAME); + + if (!inputURL.isDirectory) { + if (inputURL.fsName.equals("persistent")) { + cdvURL = scheme + "://" + hostname + "/__cdvfile_persistent__" + path; + } else if (inputURL.fsName.equals("temporary")) { + cdvURL = scheme + "://" + hostname + "/__cdvfile_temporary__" + path; + } + } + } + + entry.put("cdvURL", cdvURL); return entry; } catch (JSONException e) { e.printStackTrace(); @@ -83,12 +107,12 @@ public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri native public JSONObject makeEntryForURL(LocalFilesystemURL inputURL) { Uri nativeUri = toNativeUri(inputURL); - return nativeUri == null ? null : makeEntryForURL(inputURL, nativeUri); + return nativeUri == null ? null : makeEntryForURL(inputURL, nativeUri, preferences); } public JSONObject makeEntryForNativeUri(Uri nativeUri) { LocalFilesystemURL inputUrl = toLocalUri(nativeUri); - return inputUrl == null ? null : makeEntryForURL(inputUrl, nativeUri); + return inputUrl == null ? null : makeEntryForURL(inputUrl, nativeUri, preferences); } public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException { diff --git a/src/android/LocalFilesystem.java b/src/android/LocalFilesystem.java index 393344f4f..cee106050 100644 --- a/src/android/LocalFilesystem.java +++ b/src/android/LocalFilesystem.java @@ -28,6 +28,8 @@ Licensed to the Apache Software Foundation (ASF) under one import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; + +import org.apache.cordova.CordovaPreferences; import org.apache.cordova.CordovaResourceApi; import org.json.JSONException; import org.json.JSONObject; @@ -44,8 +46,8 @@ Licensed to the Apache Software Foundation (ASF) under one public class LocalFilesystem extends Filesystem { private final Context context; - public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot) { - super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi); + public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot, CordovaPreferences preferences) { + super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi, preferences); this.context = context; } diff --git a/www/DirectoryEntry.js b/www/DirectoryEntry.js index 26ff92451..2288eef3e 100644 --- a/www/DirectoryEntry.js +++ b/www/DirectoryEntry.js @@ -104,7 +104,7 @@ DirectoryEntry.prototype.getFile = function (path, options, successCallback, err var fs = this.filesystem; var win = successCallback && function (result) { var FileEntry = require('./FileEntry'); - var entry = new FileEntry(result.name, result.fullPath, fs, result.nativeURL); + var entry = new FileEntry(result.name, result.fullPath, fs, result.nativeURL, result.cdvURL); successCallback(entry); }; var fail = errorCallback && function (code) { diff --git a/www/Entry.js b/www/Entry.js index a67be96fa..daf5de302 100644 --- a/www/Entry.js +++ b/www/Entry.js @@ -45,13 +45,14 @@ var Metadata = require('./Metadata'); * webview controls, for example media players. * (optional, readonly) */ -function Entry (isFile, isDirectory, name, fullPath, fileSystem, nativeURL) { +function Entry (isFile, isDirectory, name, fullPath, fileSystem, nativeURL, cdvURL) { this.isFile = !!isFile; this.isDirectory = !!isDirectory; this.name = name || ''; this.fullPath = fullPath || ''; this.filesystem = fileSystem || null; this.nativeURL = nativeURL || null; + this.cdvURL = cdvURL || null; } /** @@ -198,6 +199,14 @@ Entry.prototype.toURL = function () { return this.toInternalURL() || 'file://localhost' + this.fullPath; }; +Entry.prototype.getCdvURL = function () { + if (this.cdvURL) { + return this.cdvURL; + } + + return null; +}; + /** * Backwards-compatibility: In v1.0.0 - 1.0.2, .toURL would only return a * cdvfile:// URL, and this method was necessary to obtain URLs usable by the diff --git a/www/FileEntry.js b/www/FileEntry.js index 8ed230c7a..997efd175 100644 --- a/www/FileEntry.js +++ b/www/FileEntry.js @@ -35,7 +35,7 @@ var FileError = require('./FileError'); * {DOMString} fullPath the absolute full path to the file (readonly) * {FileSystem} filesystem on which the file resides (readonly) */ -var FileEntry = function (name, fullPath, fileSystem, nativeURL) { +var FileEntry = function (name, fullPath, fileSystem, nativeURL, cdvURL) { // remove trailing slash if it is present if (fullPath && /\/$/.test(fullPath)) { fullPath = fullPath.substring(0, fullPath.length - 1); @@ -43,8 +43,11 @@ var FileEntry = function (name, fullPath, fileSystem, nativeURL) { if (nativeURL && /\/$/.test(nativeURL)) { nativeURL = nativeURL.substring(0, nativeURL.length - 1); } + if (cdvURL && /\/$/.test(cdvURL)) { + cdvURL = cdvURL.substring(0, cdvURL.length - 1); + } - FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, fileSystem, nativeURL]); + FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, fileSystem, nativeURL, cdvURL]); }; utils.extend(FileEntry, Entry); From 8a941a07594e1e9e6e07c12adaad20c282be0fbb Mon Sep 17 00:00:00 2001 From: Erisu Date: Mon, 7 Feb 2022 16:49:46 +0900 Subject: [PATCH 02/10] feat: append cdvURL to the resolveLocalFileSystemURI return FileEntity --- www/resolveLocalFileSystemURI.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/resolveLocalFileSystemURI.js b/www/resolveLocalFileSystemURI.js index abe275f2e..b91d8c94c 100644 --- a/www/resolveLocalFileSystemURI.js +++ b/www/resolveLocalFileSystemURI.js @@ -71,7 +71,7 @@ if (!fs) { fs = new FileSystem(fsName, { name: '', fullPath: '/' }); // eslint-disable-line no-undef } - var result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath, fs, entry.nativeURL) : new FileEntry(entry.name, entry.fullPath, fs, entry.nativeURL); + var result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath, fs, entry.nativeURL) : new FileEntry(entry.name, entry.fullPath, fs, entry.nativeURL, entry.cdvURL); successCallback(result); }); } From 01036fc515cad9f2e7f2af4e31f0233af1433cd7 Mon Sep 17 00:00:00 2001 From: Erisu Date: Wed, 9 Feb 2022 17:25:21 +0900 Subject: [PATCH 03/10] fix: update the fileTarget replace string --- src/android/FileUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index 1d2ee85e5..cf593ee0d 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -1262,11 +1262,11 @@ public CordovaPluginPathHandler getPathHandler() { * 4. Return the file & mime information back we Web Resources */ if (fileSystem.name.equals(targetFileSystem)) { - // replace __cdvfile_persistent__ with native path "/data/user/0/com.example.file/files/files/" + // E.g. replace __cdvfile_persistent__ with native path "/data/user/0/com.example.file/files/files/" String fileSystemNativeUri = fileSystem.rootUri.toString().replace("file://", ""); - String persistentFileTarget = path.replace("__cdvfile_persistent__/", fileSystemNativeUri); + String fileTarget = path.replace("__cdvfile_" + targetFileSystem + "__/", fileSystemNativeUri); - File file = new File(persistentFileTarget); + File file = new File(fileTarget); try { InputStream in = new FileInputStream(file); From d11fe941db55d1d10fde7965d6fae6b3a65bfb5f Mon Sep 17 00:00:00 2001 From: Erisu Date: Sat, 26 Feb 2022 13:03:51 +0900 Subject: [PATCH 04/10] chore: make androidx.webkit:webkit configurable & default to 1.4.0 --- plugin.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin.xml b/plugin.xml index edb7536ac..0e542cd05 100644 --- a/plugin.xml +++ b/plugin.xml @@ -153,7 +153,8 @@ to config.xml in order for the application to find previously stored files. - + + From 58298a4349175f41aae7ed94e59a2f26d0a9cd16 Mon Sep 17 00:00:00 2001 From: Erisu Date: Wed, 2 Mar 2022 15:37:53 +0900 Subject: [PATCH 05/10] feat: toURL to return file or custom scheme based on window location --- src/android/AssetFilesystem.java | 7 +++--- src/android/ContentFilesystem.java | 8 +++--- src/android/FileUtils.java | 35 +++++++++++++++----------- src/android/Filesystem.java | 39 ++++++++++++----------------- src/android/LocalFilesystem.java | 7 +++--- src/android/LocalFilesystemURL.java | 12 ++++++++- www/DirectoryEntry.js | 2 +- www/Entry.js | 22 ++++------------ www/FileEntry.js | 7 ++---- www/android/FileSystem.js | 2 +- www/resolveLocalFileSystemURI.js | 2 +- 11 files changed, 67 insertions(+), 76 deletions(-) diff --git a/src/android/AssetFilesystem.java b/src/android/AssetFilesystem.java index 72dbfd24b..6d766a4ad 100644 --- a/src/android/AssetFilesystem.java +++ b/src/android/AssetFilesystem.java @@ -162,10 +162,9 @@ public LocalFilesystemURL toLocalUri(Uri inputURL) { if (!subPath.isEmpty()) { subPath = subPath.substring(1); } - Uri.Builder b = new Uri.Builder() - .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL) - .authority("localhost") - .path(name); + + Uri.Builder b = createLocalUriBuilder(); + if (!subPath.isEmpty()) { b.appendEncodedPath(subPath); } diff --git a/src/android/ContentFilesystem.java b/src/android/ContentFilesystem.java index cdb5e4866..f4df440de 100644 --- a/src/android/ContentFilesystem.java +++ b/src/android/ContentFilesystem.java @@ -70,11 +70,9 @@ public LocalFilesystemURL toLocalUri(Uri inputURL) { if (subPath.length() > 0) { subPath = subPath.substring(1); } - Uri.Builder b = new Uri.Builder() - .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL) - .authority("localhost") - .path(name) - .appendPath(inputURL.getAuthority()); + + Uri.Builder b = createLocalUriBuilder().appendPath(inputURL.getAuthority()); + if (subPath.length() > 0) { b.appendEncodedPath(subPath); } diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index cf593ee0d..bfc3dc86f 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -148,7 +148,7 @@ protected void registerExtraFileSystems(String[] filesystems, HashMap or - *