From f98019ce1628884e1fbf300fcefd4b89a9839762 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Thu, 8 Jan 2026 22:26:48 +0300 Subject: [PATCH 01/12] Allow multiple trusted CORS hosts in settings --- .../routing/RouteHandler.java | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 8561e9e..48b6a4a 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -7,14 +7,17 @@ import android.content.Context; import android.content.SharedPreferences; +import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; import com.kamwithk.ankiconnectandroid.ankidroid_api.IntegratedAPI; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import fi.iki.elonen.NanoHTTPD; import fi.iki.elonen.router.RouterNanoHTTPD; @@ -66,7 +69,7 @@ public NanoHTTPD.Response get(RouterNanoHTTPD.UriResource uriResource, Map headers = session.getHeaders(); + String origin = headers.get("origin"); + if (origin == null) { + origin = headers.get("Origin"); + } + String normalizedOrigin = normalizeHost(origin); + + String[] allowedHosts = corsHostsString.split("\\r?\\n"); + List normalizedAllowedHosts = Arrays.stream(allowedHosts) + .map(this::normalizeHost) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + + // If "*" is in the allowed hosts, allow all origins + if (normalizedAllowedHosts.contains("*")) { + rep.addHeader("Access-Control-Allow-Origin", "*"); + rep.addHeader("Access-Control-Allow-Headers", "*"); + return; + } - if (!corsHost.trim().equals("")) { - rep.addHeader("Access-Control-Allow-Origin", corsHost); + // Check if the origin matches any of the allowed hosts + if (normalizedAllowedHosts.contains(normalizedOrigin)) { + rep.addHeader("Access-Control-Allow-Origin", origin); rep.addHeader("Access-Control-Allow-Headers", "*"); + return; + } + + // Else, allow the first host (to somewhat keep old behavior for backwards compatibility) + String firstHost = normalizedAllowedHosts.get(0); + rep.addHeader("Access-Control-Allow-Origin", firstHost); + rep.addHeader("Access-Control-Allow-Headers", "*"); + } + + // Trim and remove trailing slash from a host + @NonNull + private String normalizeHost(String host) { + if (host == null) { + return ""; + } + String normalizedHost = host.trim(); + if (normalizedHost.endsWith("/")) { + normalizedHost = normalizedHost.substring(0, normalizedHost.length() - 1); } + return normalizedHost; } } From 315e78afcf3a13aa0cbd2d2d4c88087909884232 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Thu, 8 Jan 2026 22:27:35 +0300 Subject: [PATCH 02/12] Adjust UI text to indicate multiple CORS hosts support --- app/src/main/res/xml/root_preferences.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index cb6a008..9214b04 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -24,10 +24,10 @@ From a322313a925a71025a50d6796dd6bd4416d632bc Mon Sep 17 00:00:00 2001 From: iekilinc Date: Thu, 8 Jan 2026 22:28:29 +0300 Subject: [PATCH 03/12] Fix CORS host preference example UI not appearing --- .../java/com/kamwithk/ankiconnectandroid/SettingsActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java index 11ec5ac..e164046 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java @@ -83,7 +83,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } - EditTextPreference corsHostPreference = findPreference("cors_hostname"); + EditTextPreference corsHostPreference = findPreference("cors_host"); if (corsHostPreference != null) { corsHostPreference.setOnBindEditTextListener(editText -> editText.setHint("e.g. http://example.com")); } From eac80c05728f032c6535efd0b2594fb75b3b8915 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Thu, 8 Jan 2026 22:39:15 +0300 Subject: [PATCH 04/12] Don't add empty string as parameter --- .../kamwithk/ankiconnectandroid/routing/RouteHandler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 48b6a4a..075f824 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -122,8 +122,10 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I // Else, allow the first host (to somewhat keep old behavior for backwards compatibility) String firstHost = normalizedAllowedHosts.get(0); - rep.addHeader("Access-Control-Allow-Origin", firstHost); - rep.addHeader("Access-Control-Allow-Headers", "*"); + if (!firstHost.isEmpty()) { + rep.addHeader("Access-Control-Allow-Origin", firstHost); + rep.addHeader("Access-Control-Allow-Headers", "*"); + } } // Trim and remove trailing slash from a host From 2c66719cd7bd101e52506a547eef1f0a52487634 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Thu, 8 Jan 2026 23:08:09 +0300 Subject: [PATCH 05/12] Revert "Don't add empty string as parameter" This reverts commit eac80c05728f032c6535efd0b2594fb75b3b8915. It's already ensured at this point that it's not empty. --- .../kamwithk/ankiconnectandroid/routing/RouteHandler.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 075f824..48b6a4a 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -122,10 +122,8 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I // Else, allow the first host (to somewhat keep old behavior for backwards compatibility) String firstHost = normalizedAllowedHosts.get(0); - if (!firstHost.isEmpty()) { - rep.addHeader("Access-Control-Allow-Origin", firstHost); - rep.addHeader("Access-Control-Allow-Headers", "*"); - } + rep.addHeader("Access-Control-Allow-Origin", firstHost); + rep.addHeader("Access-Control-Allow-Headers", "*"); } // Trim and remove trailing slash from a host From f8176cb7862b5fdab647059cd90eea9cd6ee7110 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Sun, 1 Mar 2026 22:02:40 +0300 Subject: [PATCH 06/12] Move normalizedHost to where it's actually used --- .../com/kamwithk/ankiconnectandroid/routing/RouteHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 48b6a4a..4fb6f83 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -98,7 +98,6 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I if (origin == null) { origin = headers.get("Origin"); } - String normalizedOrigin = normalizeHost(origin); String[] allowedHosts = corsHostsString.split("\\r?\\n"); List normalizedAllowedHosts = Arrays.stream(allowedHosts) @@ -114,6 +113,7 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I } // Check if the origin matches any of the allowed hosts + String normalizedOrigin = normalizeHost(origin); if (normalizedAllowedHosts.contains(normalizedOrigin)) { rep.addHeader("Access-Control-Allow-Origin", origin); rep.addHeader("Access-Control-Allow-Headers", "*"); From cd1a28f45cff3f80395502e1ff12d9f870aa39ba Mon Sep 17 00:00:00 2001 From: iekilinc Date: Sun, 1 Mar 2026 22:06:11 +0300 Subject: [PATCH 07/12] Revert "Fix CORS host preference example UI not appearing" This reverts commit a322313a925a71025a50d6796dd6bd4416d632bc. --- .../java/com/kamwithk/ankiconnectandroid/SettingsActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java index e164046..11ec5ac 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/SettingsActivity.java @@ -83,7 +83,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } - EditTextPreference corsHostPreference = findPreference("cors_host"); + EditTextPreference corsHostPreference = findPreference("cors_hostname"); if (corsHostPreference != null) { corsHostPreference.setOnBindEditTextListener(editText -> editText.setHint("e.g. http://example.com")); } From 8cbfb81ddf73a822e9d7250327917ce5ad97fbbb Mon Sep 17 00:00:00 2001 From: iekilinc Date: Sun, 1 Mar 2026 22:15:56 +0300 Subject: [PATCH 08/12] Remove duplication of adding CORS headers to response --- .../ankiconnectandroid/routing/RouteHandler.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 4fb6f83..48afbfc 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -107,22 +107,24 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I // If "*" is in the allowed hosts, allow all origins if (normalizedAllowedHosts.contains("*")) { - rep.addHeader("Access-Control-Allow-Origin", "*"); - rep.addHeader("Access-Control-Allow-Headers", "*"); + applyHeaders(rep, "*"); return; } // Check if the origin matches any of the allowed hosts String normalizedOrigin = normalizeHost(origin); if (normalizedAllowedHosts.contains(normalizedOrigin)) { - rep.addHeader("Access-Control-Allow-Origin", origin); - rep.addHeader("Access-Control-Allow-Headers", "*"); + applyHeaders(rep, origin); return; } // Else, allow the first host (to somewhat keep old behavior for backwards compatibility) String firstHost = normalizedAllowedHosts.get(0); - rep.addHeader("Access-Control-Allow-Origin", firstHost); + applyHeaders(rep, firstHost); + } + + private void applyHeaders(NanoHTTPD.Response rep, String allowOrigin) { + rep.addHeader("Access-Control-Allow-Origin", allowOrigin); rep.addHeader("Access-Control-Allow-Headers", "*"); } From 8706cd705c034b905598462a39b02ec1b58e4df7 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Sun, 1 Mar 2026 22:21:45 +0300 Subject: [PATCH 09/12] Remove redundant headers.get --- .../com/kamwithk/ankiconnectandroid/routing/RouteHandler.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 48afbfc..4be3cfe 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -95,9 +95,6 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I Map headers = session.getHeaders(); String origin = headers.get("origin"); - if (origin == null) { - origin = headers.get("Origin"); - } String[] allowedHosts = corsHostsString.split("\\r?\\n"); List normalizedAllowedHosts = Arrays.stream(allowedHosts) From 1eb9b6d036929c66ec4a0533e1e14729ea3ceba3 Mon Sep 17 00:00:00 2001 From: iekilinc Date: Sun, 1 Mar 2026 22:32:19 +0300 Subject: [PATCH 10/12] Remove redundant adding-of-first-origin --- .../com/kamwithk/ankiconnectandroid/routing/RouteHandler.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 4be3cfe..3f530e4 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -114,10 +114,6 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I applyHeaders(rep, origin); return; } - - // Else, allow the first host (to somewhat keep old behavior for backwards compatibility) - String firstHost = normalizedAllowedHosts.get(0); - applyHeaders(rep, firstHost); } private void applyHeaders(NanoHTTPD.Response rep, String allowOrigin) { From 8ca57755ba53d7a4f86e4d5987088c0e60bbb60d Mon Sep 17 00:00:00 2001 From: iekilinc Date: Sun, 1 Mar 2026 22:41:26 +0300 Subject: [PATCH 11/12] Simplify early-exiting branches since nothing runs after them --- .../ankiconnectandroid/routing/RouteHandler.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 3f530e4..52c7904 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -102,17 +102,12 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); - // If "*" is in the allowed hosts, allow all origins if (normalizedAllowedHosts.contains("*")) { + // Since "*" is in the allowed hosts, simply allow all origins applyHeaders(rep, "*"); - return; - } - - // Check if the origin matches any of the allowed hosts - String normalizedOrigin = normalizeHost(origin); - if (normalizedAllowedHosts.contains(normalizedOrigin)) { + } else if (normalizedAllowedHosts.contains(normalizeHost(origin))) { + // Request is from an origin the user trusts. applyHeaders(rep, origin); - return; } } From 38b5f0c3d1f27f684a770bd245b09d5e4b4d5daf Mon Sep 17 00:00:00 2001 From: iekilinc Date: Fri, 6 Mar 2026 14:31:57 +0300 Subject: [PATCH 12/12] Remove redundant normalizing of origin string from NanoHTTPD --- .../com/kamwithk/ankiconnectandroid/routing/RouteHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java index 52c7904..2e900c8 100644 --- a/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java +++ b/app/src/main/java/com/kamwithk/ankiconnectandroid/routing/RouteHandler.java @@ -105,7 +105,7 @@ private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.I if (normalizedAllowedHosts.contains("*")) { // Since "*" is in the allowed hosts, simply allow all origins applyHeaders(rep, "*"); - } else if (normalizedAllowedHosts.contains(normalizeHost(origin))) { + } else if (normalizedAllowedHosts.contains(origin)) { // Request is from an origin the user trusts. applyHeaders(rep, origin); }