Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -66,7 +69,7 @@ public NanoHTTPD.Response get(RouterNanoHTTPD.UriResource uriResource, Map<Strin
if (parameters == null || parameters.isEmpty() && files.get("postData") == null) {
// No data was provided in the POST request so we return a simple response
NanoHTTPD.Response rep = newFixedLengthResponse("Ankiconnect Android is running.");
addCorsHeaders(context, rep);
addCorsHeaders(context, rep, session);
return rep;
}

Expand All @@ -78,18 +81,51 @@ public NanoHTTPD.Response get(RouterNanoHTTPD.UriResource uriResource, Map<Strin
rep.addHeader(PRIVATE_NETWORK_ACCESS_RESPONSE, "true");
}

addCorsHeaders(context, rep);
addCorsHeaders(context, rep, session);
return rep;
}

private void addCorsHeaders(Context context, NanoHTTPD.Response rep) {
private void addCorsHeaders(Context context, NanoHTTPD.Response rep, NanoHTTPD.IHTTPSession session) {
// Add a CORS header if it is set in the preferences
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String corsHost = sharedPreferences.getString("cors_host", "");
String corsHostsString = sharedPreferences.getString("cors_host", "");
if (corsHostsString.trim().isEmpty()) {
return;
}

Map<String, String> headers = session.getHeaders();
String origin = headers.get("origin");

String[] allowedHosts = corsHostsString.split("\\r?\\n");
List<String> normalizedAllowedHosts = Arrays.stream(allowedHosts)
.map(this::normalizeHost)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());

if (normalizedAllowedHosts.contains("*")) {
// Since "*" is in the allowed hosts, simply allow all origins
applyHeaders(rep, "*");
} else if (normalizedAllowedHosts.contains(origin)) {
// Request is from an origin the user trusts.
applyHeaders(rep, origin);
}
}

if (!corsHost.trim().equals("")) {
rep.addHeader("Access-Control-Allow-Origin", corsHost);
rep.addHeader("Access-Control-Allow-Headers", "*");
private void applyHeaders(NanoHTTPD.Response rep, String allowOrigin) {
rep.addHeader("Access-Control-Allow-Origin", allowOrigin);
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;
}
}
6 changes: 3 additions & 3 deletions app/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@

<EditTextPreference
app:iconSpaceReserved="false"
app:dialogMessage="Enter the hostname you want to allow access from."
app:dialogTitle="Enter CORS Host"
app:dialogMessage="Enter the hostnames you want to allow access from. Each hostname should be on a new line."
app:dialogTitle="Enter CORS Hosts"
app:key="cors_host"
app:title="CORS Host"
app:title="CORS Hosts"
app:useSimpleSummaryProvider="true" />

</PreferenceCategory>
Expand Down