mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 18:07:55 +00:00
Filter device name
Following the spec: https://invent.kde.org/network/kdeconnect-meta/-/blob/master/protocol.md?ref_type=heads#kdeconnectidentity
This commit is contained in:
parent
e8f7e86b35
commit
440f1d4fa3
@ -183,7 +183,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
|||||||
<string name="pair_requested">Pair requested</string>
|
<string name="pair_requested">Pair requested</string>
|
||||||
<string name="pair_succeeded">Pair succeeded</string>
|
<string name="pair_succeeded">Pair succeeded</string>
|
||||||
<string name="pairing_verification_code" translatable="false">🔑 %1s</string>
|
<string name="pairing_verification_code" translatable="false">🔑 %1s</string>
|
||||||
<string name="pairing_request_from">Pairing request from "%1s"</string>
|
<string name="pairing_request_from">Pairing request from \'%1s\'</string>
|
||||||
<plurals name="incoming_file_title">Receiving file from %1s>
|
<plurals name="incoming_file_title">Receiving file from %1s>
|
||||||
<item quantity="one">Receiving %1$d file from %2$s</item>
|
<item quantity="one">Receiving %1$d file from %2$s</item>
|
||||||
<item quantity="other">Receiving %1$d files from %2$s</item>
|
<item quantity="other">Receiving %1$d files from %2$s</item>
|
||||||
|
@ -22,6 +22,7 @@ import android.util.Log
|
|||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import org.kde.kdeconnect.Backends.BaseLinkProvider
|
import org.kde.kdeconnect.Backends.BaseLinkProvider
|
||||||
import org.kde.kdeconnect.Device
|
import org.kde.kdeconnect.Device
|
||||||
|
import org.kde.kdeconnect.DeviceInfo
|
||||||
import org.kde.kdeconnect.DeviceInfo.Companion.fromIdentityPacketAndCert
|
import org.kde.kdeconnect.DeviceInfo.Companion.fromIdentityPacketAndCert
|
||||||
import org.kde.kdeconnect.Helpers.DeviceHelper
|
import org.kde.kdeconnect.Helpers.DeviceHelper
|
||||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
||||||
@ -44,6 +45,12 @@ class BluetoothLinkProvider(private val context: Context) : BaseLinkProvider() {
|
|||||||
|
|
||||||
@Throws(CertificateException::class)
|
@Throws(CertificateException::class)
|
||||||
private fun addLink(identityPacket: NetworkPacket, link: BluetoothLink) {
|
private fun addLink(identityPacket: NetworkPacket, link: BluetoothLink) {
|
||||||
|
|
||||||
|
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
||||||
|
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val deviceId = identityPacket.getString("deviceId")
|
val deviceId = identityPacket.getString("deviceId")
|
||||||
Log.i("BluetoothLinkProvider", "addLink to $deviceId")
|
Log.i("BluetoothLinkProvider", "addLink to $deviceId")
|
||||||
val oldLink = visibleDevices[deviceId]
|
val oldLink = visibleDevices[deviceId]
|
||||||
@ -369,6 +376,13 @@ class BluetoothLinkProvider(private val context: Context) : BaseLinkProvider() {
|
|||||||
socket.close()
|
socket.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
||||||
|
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.")
|
||||||
|
connection.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Log.i("BTLinkProvider/Client", "Received identity packet")
|
Log.i("BTLinkProvider/Client", "Received identity packet")
|
||||||
val myId = DeviceHelper.getDeviceId(context)
|
val myId = DeviceHelper.getDeviceId(context)
|
||||||
if (identityPacket.getString("deviceId") == myId) {
|
if (identityPacket.getString("deviceId") == myId) {
|
||||||
|
@ -125,6 +125,12 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
|
|
||||||
String message = new String(packet.getData(), Charsets.UTF_8);
|
String message = new String(packet.getData(), Charsets.UTF_8);
|
||||||
final NetworkPacket identityPacket = NetworkPacket.unserialize(message);
|
final NetworkPacket identityPacket = NetworkPacket.unserialize(message);
|
||||||
|
|
||||||
|
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
||||||
|
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final String deviceId = identityPacket.getString("deviceId");
|
final String deviceId = identityPacket.getString("deviceId");
|
||||||
if (!identityPacket.getType().equals(NetworkPacket.PACKET_TYPE_IDENTITY)) {
|
if (!identityPacket.getType().equals(NetworkPacket.PACKET_TYPE_IDENTITY)) {
|
||||||
Log.e("KDE/LanLinkProvider", "Expecting an UDP identity packet");
|
Log.e("KDE/LanLinkProvider", "Expecting an UDP identity packet");
|
||||||
@ -192,6 +198,11 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
@WorkerThread
|
@WorkerThread
|
||||||
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted) throws IOException {
|
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted) throws IOException {
|
||||||
|
|
||||||
|
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
||||||
|
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String myId = DeviceHelper.getDeviceId(context);
|
String myId = DeviceHelper.getDeviceId(context);
|
||||||
final String deviceId = identityPacket.getString("deviceId");
|
final String deviceId = identityPacket.getString("deviceId");
|
||||||
if (deviceId.equals(myId)) {
|
if (deviceId.equals(myId)) {
|
||||||
|
@ -10,6 +10,7 @@ import android.content.Context
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.kde.kdeconnect.Helpers.DeviceHelper
|
||||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
||||||
import org.kde.kdeconnect_tp.R
|
import org.kde.kdeconnect_tp.R
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
@ -90,7 +91,7 @@ class DeviceInfo(
|
|||||||
with(identityPacket) {
|
with(identityPacket) {
|
||||||
DeviceInfo(
|
DeviceInfo(
|
||||||
id = getString("deviceId"), // Redundant: We could read this from the certificate instead
|
id = getString("deviceId"), // Redundant: We could read this from the certificate instead
|
||||||
name = getString("deviceName", "unknown"),
|
name = DeviceHelper.filterName(getString("deviceName", "unknown")),
|
||||||
type = DeviceType.fromString(getString("deviceType", "desktop")),
|
type = DeviceType.fromString(getString("deviceType", "desktop")),
|
||||||
certificate = certificate,
|
certificate = certificate,
|
||||||
protocolVersion = getInt("protocolVersion"),
|
protocolVersion = getInt("protocolVersion"),
|
||||||
@ -98,6 +99,11 @@ class DeviceInfo(
|
|||||||
outgoingCapabilities = getStringSet("outgoingCapabilities")
|
outgoingCapabilities = getStringSet("outgoingCapabilities")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isValidIdentityPacket(identityPacket: NetworkPacket): Boolean = with(identityPacket) {
|
||||||
|
DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() && getString("deviceId", "").isNotBlank()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,9 @@ object DeviceHelper {
|
|||||||
|
|
||||||
private const val DEVICE_DATABASE = "https://storage.googleapis.com/play_public/supported_devices.csv"
|
private const val DEVICE_DATABASE = "https://storage.googleapis.com/play_public/supported_devices.csv"
|
||||||
|
|
||||||
|
private val NAME_INVALID_CHARACTERS_REGEX = "[\"',;:.!?()\\[\\]<>]".toRegex()
|
||||||
|
const val MAX_DEVICE_NAME_LENGTH = 32
|
||||||
|
|
||||||
private val isTablet: Boolean by lazy {
|
private val isTablet: Boolean by lazy {
|
||||||
val config = Resources.getSystem().configuration
|
val config = Resources.getSystem().configuration
|
||||||
//This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE
|
//This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE
|
||||||
@ -119,8 +122,9 @@ object DeviceHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setDeviceName(context: Context, name: String) {
|
fun setDeviceName(context: Context, name: String) {
|
||||||
|
val filteredName = filterName(name)
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, name).apply()
|
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, filteredName).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initializeDeviceId(context: Context) {
|
fun initializeDeviceId(context: Context) {
|
||||||
@ -160,4 +164,7 @@ object DeviceHelper {
|
|||||||
PluginFactory.getOutgoingCapabilities()
|
PluginFactory.getOutgoingCapabilities()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun filterName(input: String): String = input.replace(NAME_INVALID_CHARACTERS_REGEX, "").take(MAX_DEVICE_NAME_LENGTH)
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,13 @@
|
|||||||
package org.kde.kdeconnect.UserInterface;
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.InputFilter;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -33,6 +32,7 @@ import androidx.preference.TwoStatePreference;
|
|||||||
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.kde.kdeconnect.BackgroundService;
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||||
@ -66,6 +66,10 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
renameDevice.setKey(DeviceHelper.KEY_DEVICE_NAME_PREFERENCE);
|
renameDevice.setKey(DeviceHelper.KEY_DEVICE_NAME_PREFERENCE);
|
||||||
renameDevice.setSelectable(true);
|
renameDevice.setSelectable(true);
|
||||||
renameDevice.setOnBindEditTextListener(TextView::setSingleLine);
|
renameDevice.setOnBindEditTextListener(TextView::setSingleLine);
|
||||||
|
renameDevice.setOnBindEditTextListener(editText -> editText.setFilters(new InputFilter[] {
|
||||||
|
(source, start, end, dest, dstart, dend) -> DeviceHelper.filterName(source.subSequence(start, end).toString()),
|
||||||
|
new InputFilter.LengthFilter(DeviceHelper.MAX_DEVICE_NAME_LENGTH),
|
||||||
|
}));
|
||||||
String deviceName = DeviceHelper.getDeviceName(context);
|
String deviceName = DeviceHelper.getDeviceName(context);
|
||||||
renameDevice.setTitle(R.string.settings_rename);
|
renameDevice.setTitle(R.string.settings_rename);
|
||||||
renameDevice.setSummary(deviceName);
|
renameDevice.setSummary(deviceName);
|
||||||
@ -76,7 +80,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||||||
renameDevice.setOnPreferenceChangeListener((preference, newValue) -> {
|
renameDevice.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
String name = (String) newValue;
|
String name = (String) newValue;
|
||||||
|
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (StringUtils.isBlank(name)) {
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
Snackbar snackbar = Snackbar.make(getView(), R.string.invalid_device_name, Snackbar.LENGTH_LONG);
|
Snackbar snackbar = Snackbar.make(getView(), R.string.invalid_device_name, Snackbar.LENGTH_LONG);
|
||||||
int currentTheme = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
int currentTheme = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
@ -132,6 +132,27 @@ public class DeviceTest {
|
|||||||
assertEquals(di.outgoingCapabilities, np.getStringSet("outgoingCapabilities"));
|
assertEquals(di.outgoingCapabilities, np.getStringSet("outgoingCapabilities"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsValidIdentityPacket() {
|
||||||
|
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY);
|
||||||
|
assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np));
|
||||||
|
|
||||||
|
String validName = "MyDevice";
|
||||||
|
String validId = "123";
|
||||||
|
np.set("deviceName", validName);
|
||||||
|
np.set("deviceId", validId);
|
||||||
|
assertTrue(DeviceInfo.Companion.isValidIdentityPacket(np));
|
||||||
|
|
||||||
|
np.set("deviceName", " ");
|
||||||
|
assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np));
|
||||||
|
np.set("deviceName", "<><><><><><><><><>"); // Only invalid characters
|
||||||
|
assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np));
|
||||||
|
|
||||||
|
np.set("deviceName", validName);
|
||||||
|
np.set("deviceId", " ");
|
||||||
|
assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeviceType() {
|
public void testDeviceType() {
|
||||||
assertEquals(DeviceType.PHONE, DeviceType.fromString(DeviceType.PHONE.toString()));
|
assertEquals(DeviceType.PHONE, DeviceType.fromString(DeviceType.PHONE.toString()));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user