From 5fbae2240b3710a3004d4a5a282e071bec2737d4 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 08:12:21 -0400 Subject: [PATCH 01/16] feat: Authenticate private resource requests Unauthenticated requests failed when attempting to load resources from a private site, e.g., an image. --- .../android/ui/posts/EditPostActivity.kt | 1 + .../gutenberg/GutenbergEditorFragment.java | 62 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt index 20c2710b1651..a79a86e81d05 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt @@ -2444,6 +2444,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm "postType" to postType, "postTitle" to editPostRepository.getPost()?.title, "postContent" to editPostRepository.getPost()?.content, + "siteURL" to site.url, "siteApiRoot" to siteApiRoot, "authHeader" to authHeader, "siteApiNamespace" to siteApiNamespace, diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index fabee0a89ddc..12eadacbb3b7 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -1,5 +1,11 @@ package org.wordpress.android.editor.gutenberg; +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; import android.app.Activity; import android.app.ProgressDialog; import android.content.ClipData; @@ -21,6 +27,9 @@ import android.view.inputmethod.InputMethodManager; import android.webkit.URLUtil; import android.webkit.ValueCallback; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -68,6 +77,7 @@ import org.wordpress.android.util.helpers.MediaFile; import org.wordpress.android.util.helpers.MediaGallery; import org.wordpress.aztec.IHistoryListener; +import org.wordpress.gutenberg.GutenbergRequestInterceptor; import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.LogExceptionCallback; import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity; import org.wordpress.mobile.WPAndroidGlue.GutenbergJsException; @@ -97,6 +107,7 @@ import org.wordpress.gutenberg.GutenbergView.ContentChangeListener; import org.wordpress.gutenberg.GutenbergWebViewPool; +import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; @@ -104,9 +115,12 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType; @@ -117,7 +131,8 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements EditorThemeUpdateListener, GutenbergDialogPositiveClickInterface, GutenbergDialogNegativeClickInterface, - GutenbergNetworkConnectionListener { + GutenbergNetworkConnectionListener, + GutenbergRequestInterceptor { @Nullable private GutenbergView mGutenbergView; private static final String GUTENBERG_EDITOR_NAME = "gutenberg"; private static final String KEY_HTML_MODE_ENABLED = "KEY_HTML_MODE_ENABLED"; @@ -291,6 +306,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mGutenbergView.setEditorDidBecomeAvailable(view -> { mEditorFragmentListener.onEditorFragmentContentReady(new ArrayList(), false); }); + mGutenbergView.setRequestInterceptor(this); Integer postId = (Integer) mSettings.get("postId"); if (postId != null && postId == 0) { @@ -1635,4 +1651,48 @@ public void onConnectionStatusChange(boolean isConnected) { mEditorFragmentListener.onMediaRetryAll(mFailedMediaIds); } } + + @Nullable @Override + public WebResourceResponse modifyRequest(@NonNull WebView view, @NonNull WebResourceRequest request) { + Uri url = request.getUrl(); + String siteURL = (String) (mSettings != null ? mSettings.get("siteURL") : ""); + String regex = siteURL + "/.*\\.(jpg|jpeg|png|gif|bmp|webp|mp4|mov|avi|mkv|mp3|wav|flac)(\\?.*)?$"; + Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(url.toString()); + + if (siteURL == null || !matcher.matches()) { + return null; + } + + try { + Request okHttpRequest = new Request.Builder() + .url(url.toString()) + .headers(Headers.of(request.getRequestHeaders())) + .addHeader("Authorization", mSettings.get("authHeader").toString()) + .build(); + + OkHttpClient httpClient = new OkHttpClient(); + Response response = httpClient.newCall(okHttpRequest).execute(); + + ResponseBody body = response.body(); + if (body == null) { + return null; + } + + okhttp3.MediaType contentType = body.contentType(); + if (contentType == null) { + return null; + } + + return new WebResourceResponse( + contentType.toString(), + response.header("content-encoding"), + body.byteStream() + ); + } catch (IOException e) { + // We don't need to handle this ourselves, just tell the WebView that + // we weren't able to fetch the resource + return null; + } + } } From fca9c93492676fa1efcc349dd5f3eb69f219c6cc Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 20:32:54 -0400 Subject: [PATCH 02/16] build: Update GutenbergKit ref --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8303e3ca5a98..fdcde99c4457 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '1.0.0' greenrobot-eventbus = '3.3.1' -gutenberg-kit = 'trunk-a58a46f3fbb892f311b562e3c122d7ef4ebbfe33' +gutenberg-kit = '34-8f4d82fedf9bd3d845efab060d53d020921904f7' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1' From 1e3a728f5ba950c129b3410d7e7d315c25b0b48a Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 20:43:07 -0400 Subject: [PATCH 03/16] refactor: Remove unused imports --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 12eadacbb3b7..3fc57211cec9 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -1,7 +1,6 @@ package org.wordpress.android.editor.gutenberg; import okhttp3.Headers; -import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -115,7 +114,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; From 3df96656b246aa78569cd988f17618b51af7b42a Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 20:43:19 -0400 Subject: [PATCH 04/16] perf: Avoid re-instantiating HTTP client on each invocation --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 3fc57211cec9..aa3462e7c3ef 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -194,6 +194,7 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private ProgressDialog mSavingContentProgressDialog; @Nullable private static Map mSettings; + OkHttpClient mHttpClient = new OkHttpClient(); public static GutenbergEditorFragment newInstance(Context context, boolean isNewPost, @@ -1669,8 +1670,7 @@ public WebResourceResponse modifyRequest(@NonNull WebView view, @NonNull WebReso .addHeader("Authorization", mSettings.get("authHeader").toString()) .build(); - OkHttpClient httpClient = new OkHttpClient(); - Response response = httpClient.newCall(okHttpRequest).execute(); + Response response = mHttpClient.newCall(okHttpRequest).execute(); ResponseBody body = response.body(); if (body == null) { From e6eebbce1c4f4da5755898597da97d39411225e2 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 20:44:04 -0400 Subject: [PATCH 05/16] build: Update GutenbergKit ref --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fdcde99c4457..83cfc873ec9f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '1.0.0' greenrobot-eventbus = '3.3.1' -gutenberg-kit = '34-8f4d82fedf9bd3d845efab060d53d020921904f7' +gutenberg-kit = '34-b2ae11f9c3d2d8d4ab7770ec6a12bd77d5e1263f' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1' From cf65616c05e20b31ab68a28891db1189fba10a52 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 21:08:17 -0400 Subject: [PATCH 06/16] style: Address lint import order warning --- .../editor/gutenberg/GutenbergEditorFragment.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index aa3462e7c3ef..87ce1617f47a 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -1,10 +1,5 @@ package org.wordpress.android.editor.gutenberg; -import okhttp3.Headers; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; import android.app.Activity; import android.app.ProgressDialog; import android.content.ClipData; @@ -121,6 +116,12 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import okhttp3.Headers; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType; public class GutenbergEditorFragment extends EditorFragmentAbstract implements From 3db1ad26d21a7104d90022d1bed625b3e3346c5b Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 21:08:47 -0400 Subject: [PATCH 07/16] style: Address lint nullable warning --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 87ce1617f47a..d3fd61d5f5aa 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -195,7 +195,7 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private ProgressDialog mSavingContentProgressDialog; @Nullable private static Map mSettings; - OkHttpClient mHttpClient = new OkHttpClient(); + @NonNull OkHttpClient mHttpClient = new OkHttpClient(); public static GutenbergEditorFragment newInstance(Context context, boolean isNewPost, From 51ccd402871cb0a1361f97da7154bade12781404 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 22 Oct 2024 21:09:35 -0400 Subject: [PATCH 08/16] build: Update GutenbergKit ref --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 83cfc873ec9f..07e0c5ab8005 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '1.0.0' greenrobot-eventbus = '3.3.1' -gutenberg-kit = '34-b2ae11f9c3d2d8d4ab7770ec6a12bd77d5e1263f' +gutenberg-kit = '34-75b6982bf1c96c6778dbaa6bfdd9bfc72b3a49fc' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1' From f794e1029eef293f191ce455993d121a65d8925b Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 23 Oct 2024 15:59:22 -0400 Subject: [PATCH 09/16] fix: Proxy private Atomic resource requests The proxy is required to authenticate the request with a bearer token. --- .../android/ui/posts/EditPostActivity.kt | 4 +- .../gutenberg/GutenbergEditorFragment.java | 37 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt index a79a86e81d05..f65db2d5377c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt @@ -2458,7 +2458,9 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm gutenbergPropsBuilder, jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures(), isNewGutenbergEditor, - settings + settings, + site.isPrivate || site.isComingSoon, + site.isPrivateWPComAtomic ) } diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index d3fd61d5f5aa..688b02f1f26a 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -195,6 +195,8 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private ProgressDialog mSavingContentProgressDialog; @Nullable private static Map mSettings; + private static boolean mIsPrivate = false; + private static boolean mIsPrivateAtomic = false; @NonNull OkHttpClient mHttpClient = new OkHttpClient(); public static GutenbergEditorFragment newInstance(Context context, @@ -203,7 +205,9 @@ public static GutenbergEditorFragment newInstance(Context context, GutenbergPropsBuilder gutenbergPropsBuilder, boolean jetpackFeaturesEnabled, boolean newGutenbergEnabled, - @Nullable Map settings) { + @Nullable Map settings, + boolean isPrivate, + boolean isPrivateAtomic) { GutenbergEditorFragment fragment = new GutenbergEditorFragment(); Bundle args = new Bundle(); args.putBoolean(ARG_IS_NEW_POST, isNewPost); @@ -214,6 +218,8 @@ public static GutenbergEditorFragment newInstance(Context context, SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(context); mIsNewGutenbergEnabled = newGutenbergEnabled; mSettings = settings; + mIsPrivate = isPrivate; + mIsPrivateAtomic = isPrivateAtomic; if (db != null) { db.addParcel(ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, webViewAuthorizationData); db.addParcel(ARG_GUTENBERG_PROPS_BUILDER, gutenbergPropsBuilder); @@ -1656,17 +1662,22 @@ public void onConnectionStatusChange(boolean isConnected) { public WebResourceResponse modifyRequest(@NonNull WebView view, @NonNull WebResourceRequest request) { Uri url = request.getUrl(); String siteURL = (String) (mSettings != null ? mSettings.get("siteURL") : ""); - String regex = siteURL + "/.*\\.(jpg|jpeg|png|gif|bmp|webp|mp4|mov|avi|mkv|mp3|wav|flac)(\\?.*)?$"; - Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + String siteHostedMedia = siteURL + "/.*\\.(jpg|jpeg|png|gif|bmp|webp|mp4|mov|avi|mkv|mp3|wav|flac)(\\?.*)?$"; + Pattern pattern = Pattern.compile(siteHostedMedia, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(url.toString()); - if (siteURL == null || !matcher.matches()) { + if (siteURL == null || !mIsPrivate || !matcher.matches()) { return null; } + String proxyUrl = url.toString(); + if (mIsPrivateAtomic) { + proxyUrl = getPrivateResourceProxyUrl(url); + } + try { Request okHttpRequest = new Request.Builder() - .url(url.toString()) + .url(proxyUrl) .headers(Headers.of(request.getRequestHeaders())) .addHeader("Authorization", mSettings.get("authHeader").toString()) .build(); @@ -1694,4 +1705,20 @@ public WebResourceResponse modifyRequest(@NonNull WebView view, @NonNull WebReso return null; } } + + private static @NonNull String getPrivateResourceProxyUrl(@NonNull Uri url) { + Uri newUri = new Uri.Builder() + .scheme("https") + .authority("public-api.wordpress.com") + .appendPath("wpcom") + .appendPath("v2") + .appendPath("sites") + .appendPath(url.getAuthority()) + .appendPath("atomic-auth-proxy") + .appendPath("file") + .appendEncodedPath(url.getPath().substring(1)) // Remove leading '/' + .build(); + + return newUri.toString(); + } } From 8d2a15080f788928ccf3acf3acd80569c4048cae Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 24 Oct 2024 11:13:29 -0400 Subject: [PATCH 10/16] refactor: Remove unused view parameter This was added to mirror the `shouldInterceptRequest` method signature, but is unnecessary complexity. --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 688b02f1f26a..ca8c7ae6695a 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -1659,7 +1659,7 @@ public void onConnectionStatusChange(boolean isConnected) { } @Nullable @Override - public WebResourceResponse modifyRequest(@NonNull WebView view, @NonNull WebResourceRequest request) { + public WebResourceResponse modifyRequest(@NonNull WebResourceRequest request) { Uri url = request.getUrl(); String siteURL = (String) (mSettings != null ? mSettings.get("siteURL") : ""); String siteHostedMedia = siteURL + "/.*\\.(jpg|jpeg|png|gif|bmp|webp|mp4|mov|avi|mkv|mp3|wav|flac)(\\?.*)?$"; From 4ecab435b1ae1a4dd230cdf03acbc424a5fa3493 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 24 Oct 2024 11:38:10 -0400 Subject: [PATCH 11/16] build: Update GutenbergKit ref --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07e0c5ab8005..ea1bf4427b00 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '1.0.0' greenrobot-eventbus = '3.3.1' -gutenberg-kit = '34-75b6982bf1c96c6778dbaa6bfdd9bfc72b3a49fc' +gutenberg-kit = '34-946b8c24e4e55e42304fa0821d27307915ef9cb8' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1' From 7f12e5a9aa02853d76278df1c5e7ac5308fd6990 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 24 Oct 2024 11:49:25 -0400 Subject: [PATCH 12/16] style: Remove unused import --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index ca8c7ae6695a..d109059e51ed 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -23,7 +23,6 @@ import android.webkit.ValueCallback; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; -import android.webkit.WebView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; From 30a8991c38fca941ca7b1c6ad688285c42fce840 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 30 Oct 2024 11:40:49 -0400 Subject: [PATCH 13/16] refactor: Separate check and handle request methods Mirror the iOS method structure. --- gradle/libs.versions.toml | 2 +- .../editor/gutenberg/GutenbergEditorFragment.java | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea1bf4427b00..59ccd51e78e7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '1.0.0' greenrobot-eventbus = '3.3.1' -gutenberg-kit = '34-946b8c24e4e55e42304fa0821d27307915ef9cb8' +gutenberg-kit = '34-189ca417f1ca130c579ef70f7006a6a67ae6b842' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1' diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index d109059e51ed..96b4e6324f91 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -1657,17 +1657,20 @@ public void onConnectionStatusChange(boolean isConnected) { } } - @Nullable @Override - public WebResourceResponse modifyRequest(@NonNull WebResourceRequest request) { + @Override + public boolean canIntercept(@NonNull WebResourceRequest request) { Uri url = request.getUrl(); String siteURL = (String) (mSettings != null ? mSettings.get("siteURL") : ""); String siteHostedMedia = siteURL + "/.*\\.(jpg|jpeg|png|gif|bmp|webp|mp4|mov|avi|mkv|mp3|wav|flac)(\\?.*)?$"; Pattern pattern = Pattern.compile(siteHostedMedia, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(url.toString()); - if (siteURL == null || !mIsPrivate || !matcher.matches()) { - return null; - } + return siteURL != null && mIsPrivate && matcher.matches(); + } + + @Nullable @Override + public WebResourceResponse handleRequest(@NonNull WebResourceRequest request) { + Uri url = request.getUrl(); String proxyUrl = url.toString(); if (mIsPrivateAtomic) { From a9fe1d56d3b4b7c5796337f27c463095400a5ddb Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 30 Oct 2024 16:23:38 -0400 Subject: [PATCH 14/16] refactor: Replace RegEx with URL parsing Likely better performance and accuracy. --- .../gutenberg/GutenbergEditorFragment.java | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 96b4e6324f91..26225523947d 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -102,7 +102,10 @@ import java.io.IOException; import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -111,8 +114,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import okhttp3.Headers; @@ -1660,12 +1661,42 @@ public void onConnectionStatusChange(boolean isConnected) { @Override public boolean canIntercept(@NonNull WebResourceRequest request) { Uri url = request.getUrl(); + + return mIsPrivate && isSiteHostedMediaFile(url.toString()); + } + + boolean isSiteHostedMediaFile(@NonNull String urlString) { String siteURL = (String) (mSettings != null ? mSettings.get("siteURL") : ""); - String siteHostedMedia = siteURL + "/.*\\.(jpg|jpeg|png|gif|bmp|webp|mp4|mov|avi|mkv|mp3|wav|flac)(\\?.*)?$"; - Pattern pattern = Pattern.compile(siteHostedMedia, Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(url.toString()); + Set mediaExtensions = new HashSet<>(Arrays.asList( + "jpg", "jpeg", "png", "gif", "bmp", "webp", + "mp4", "mov", "avi", "mkv", + "mp3", "wav", "flac" + )); + + try { + URL url = new URL(urlString); + URL siteUrlObj = new URL(siteURL); + + // Check if the URL is from the same host as the site URL + if (!url.getHost().equalsIgnoreCase(siteUrlObj.getHost())) { + return false; + } + + // Extract the file name and extension + String path = url.getPath(); + int lastDotIndex = path.lastIndexOf('.'); + if (lastDotIndex == -1) { + return false; + } - return siteURL != null && mIsPrivate && matcher.matches(); + String extension = path.substring(lastDotIndex + 1).toLowerCase(); + + // Check if the extension is in the list of media extensions + return mediaExtensions.contains(extension); + } catch (MalformedURLException e) { + // Handle invalid URLs + return false; + } } @Nullable @Override From 9c41ed32b0a8413e96d9e829b992994f14f14f25 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 30 Oct 2024 21:18:34 -0400 Subject: [PATCH 15/16] fix: Avoid implicit use of default locale Address lint error regarding a common source of bugs. https://github.com/wordpress-mobile/WordPress-Android/pull/21331#pullrequestreview-2406278471 --- .../android/editor/gutenberg/GutenbergEditorFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 26225523947d..8e25e9b265cc 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -110,6 +110,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -1689,7 +1690,7 @@ boolean isSiteHostedMediaFile(@NonNull String urlString) { return false; } - String extension = path.substring(lastDotIndex + 1).toLowerCase(); + String extension = path.substring(lastDotIndex + 1).toLowerCase(Locale.ROOT); // Check if the extension is in the list of media extensions return mediaExtensions.contains(extension); From 554d6ca86caba79f707ecd7ffc056d26e07c2d8a Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Fri, 1 Nov 2024 09:50:40 -0400 Subject: [PATCH 16/16] build: Update GutenbergKit ref --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 59ccd51e78e7..73b141d8e311 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '1.0.0' greenrobot-eventbus = '3.3.1' -gutenberg-kit = '34-189ca417f1ca130c579ef70f7006a6a67ae6b842' +gutenberg-kit = 'trunk-d6fbfc7bc28ae6db2cce09950f24bc3080374596' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1'