mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-03 00:06:34 +00:00
fix: support host names with _
Use a JSON to properly encapsule different computer addresses and their port, instead of using "_" as separator. Fix usage of '_' in computer host names / domain names.
This commit is contained in:
parent
c8198b4091
commit
3a9eabf50b
@ -9,7 +9,9 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
import com.limelight.nvstream.http.LimelightCryptoProvider;
|
||||||
import com.limelight.nvstream.http.NvHTTP;
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@ -18,18 +20,29 @@ import android.database.Cursor;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class ComputerDatabaseManager {
|
public class ComputerDatabaseManager {
|
||||||
private static final String COMPUTER_DB_NAME = "computers3.db";
|
private static final String COMPUTER_DB_NAME = "computers4.db";
|
||||||
private static final String COMPUTER_TABLE_NAME = "Computers";
|
private static final String COMPUTER_TABLE_NAME = "Computers";
|
||||||
private static final String COMPUTER_UUID_COLUMN_NAME = "UUID";
|
private static final String COMPUTER_UUID_COLUMN_NAME = "UUID";
|
||||||
private static final String COMPUTER_NAME_COLUMN_NAME = "ComputerName";
|
private static final String COMPUTER_NAME_COLUMN_NAME = "ComputerName";
|
||||||
private static final String ADDRESSES_COLUMN_NAME = "Addresses";
|
private static final String ADDRESSES_COLUMN_NAME = "Addresses";
|
||||||
|
private interface AddressFields {
|
||||||
|
String LOCAL = "local";
|
||||||
|
String REMOTE = "remote";
|
||||||
|
String MANUAL = "manual";
|
||||||
|
String IPv6 = "ipv6";
|
||||||
|
|
||||||
|
String ADDRESS = "address";
|
||||||
|
String PORT = "port";
|
||||||
|
}
|
||||||
|
|
||||||
private static final String MAC_ADDRESS_COLUMN_NAME = "MacAddress";
|
private static final String MAC_ADDRESS_COLUMN_NAME = "MacAddress";
|
||||||
private static final String SERVER_CERT_COLUMN_NAME = "ServerCert";
|
private static final String SERVER_CERT_COLUMN_NAME = "ServerCert";
|
||||||
|
|
||||||
private static final char ADDRESS_DELIMITER = ';';
|
|
||||||
private static final char PORT_DELIMITER = '_';
|
|
||||||
|
|
||||||
private SQLiteDatabase computerDb;
|
private SQLiteDatabase computerDb;
|
||||||
|
|
||||||
public ComputerDatabaseManager(Context c) {
|
public ComputerDatabaseManager(Context c) {
|
||||||
@ -64,24 +77,52 @@ public class ComputerDatabaseManager {
|
|||||||
for (ComputerDetails computer : oldComputers) {
|
for (ComputerDetails computer : oldComputers) {
|
||||||
updateComputer(computer);
|
updateComputer(computer);
|
||||||
}
|
}
|
||||||
|
oldComputers = LegacyDatabaseReader3.migrateAllComputers(c);
|
||||||
|
for (ComputerDetails computer : oldComputers) {
|
||||||
|
updateComputer(computer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteComputer(ComputerDetails details) {
|
public void deleteComputer(ComputerDetails details) {
|
||||||
computerDb.delete(COMPUTER_TABLE_NAME, COMPUTER_UUID_COLUMN_NAME+"=?", new String[]{details.uuid});
|
computerDb.delete(COMPUTER_TABLE_NAME, COMPUTER_UUID_COLUMN_NAME+"=?", new String[]{details.uuid});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JSONObject tupleToJson(ComputerDetails.AddressTuple tuple) throws JSONException {
|
||||||
|
if (tuple == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put(AddressFields.ADDRESS, tuple.address);
|
||||||
|
json.put(AddressFields.PORT, tuple.port);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ComputerDetails.AddressTuple tupleFromJson(JSONObject json) throws JSONException {
|
||||||
|
if (json == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComputerDetails.AddressTuple(
|
||||||
|
json.getString(AddressFields.ADDRESS), json.getInt(AddressFields.PORT));
|
||||||
|
}
|
||||||
|
|
||||||
public boolean updateComputer(ComputerDetails details) {
|
public boolean updateComputer(ComputerDetails details) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(COMPUTER_UUID_COLUMN_NAME, details.uuid);
|
values.put(COMPUTER_UUID_COLUMN_NAME, details.uuid);
|
||||||
values.put(COMPUTER_NAME_COLUMN_NAME, details.name);
|
values.put(COMPUTER_NAME_COLUMN_NAME, details.name);
|
||||||
|
|
||||||
StringBuilder addresses = new StringBuilder();
|
try {
|
||||||
addresses.append(details.localAddress != null ? splitTupleToAddress(details.localAddress) : "");
|
JSONObject addresses = new JSONObject();
|
||||||
addresses.append(ADDRESS_DELIMITER).append(details.remoteAddress != null ? splitTupleToAddress(details.remoteAddress) : "");
|
addresses.put(AddressFields.LOCAL, tupleToJson((details.localAddress)));
|
||||||
addresses.append(ADDRESS_DELIMITER).append(details.manualAddress != null ? splitTupleToAddress(details.manualAddress) : "");
|
addresses.put(AddressFields.REMOTE, tupleToJson((details.remoteAddress)));
|
||||||
addresses.append(ADDRESS_DELIMITER).append(details.ipv6Address != null ? splitTupleToAddress(details.ipv6Address) : "");
|
addresses.put(AddressFields.MANUAL, tupleToJson((details.manualAddress)));
|
||||||
|
addresses.put(AddressFields.IPv6, tupleToJson((details.ipv6Address)));
|
||||||
values.put(ADDRESSES_COLUMN_NAME, addresses.toString());
|
values.put(ADDRESSES_COLUMN_NAME, addresses.toString());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
LimeLog.warning("JSON error, failed to write computer address information, " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress);
|
values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress);
|
||||||
try {
|
try {
|
||||||
if (details.serverCert != null) {
|
if (details.serverCert != null) {
|
||||||
@ -105,36 +146,20 @@ public class ComputerDatabaseManager {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ComputerDetails.AddressTuple splitAddressToTuple(String input) {
|
|
||||||
if (input == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] parts = input.split(""+PORT_DELIMITER, -1);
|
|
||||||
if (parts.length == 1) {
|
|
||||||
return new ComputerDetails.AddressTuple(parts[0], NvHTTP.DEFAULT_HTTP_PORT);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new ComputerDetails.AddressTuple(parts[0], Integer.parseInt(parts[1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String splitTupleToAddress(ComputerDetails.AddressTuple tuple) {
|
|
||||||
return tuple.address+PORT_DELIMITER+tuple.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ComputerDetails getComputerFromCursor(Cursor c) {
|
private ComputerDetails getComputerFromCursor(Cursor c) {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
|
|
||||||
details.uuid = c.getString(0);
|
details.uuid = c.getString(0);
|
||||||
details.name = c.getString(1);
|
details.name = c.getString(1);
|
||||||
|
try {
|
||||||
String[] addresses = c.getString(2).split(""+ADDRESS_DELIMITER, -1);
|
JSONObject addresses = new JSONObject(c.getString(2));
|
||||||
|
details.localAddress = tupleFromJson(addresses.getJSONObject(AddressFields.LOCAL));
|
||||||
details.localAddress = splitAddressToTuple(readNonEmptyString(addresses[0]));
|
details.remoteAddress = tupleFromJson(addresses.getJSONObject(AddressFields.REMOTE));
|
||||||
details.remoteAddress = splitAddressToTuple(readNonEmptyString(addresses[1]));
|
details.manualAddress = tupleFromJson(addresses.getJSONObject(AddressFields.MANUAL));
|
||||||
details.manualAddress = splitAddressToTuple(readNonEmptyString(addresses[2]));
|
details.ipv6Address = tupleFromJson(addresses.getJSONObject(AddressFields.IPv6));
|
||||||
details.ipv6Address = splitAddressToTuple(readNonEmptyString(addresses[3]));
|
} catch (JSONException e) {
|
||||||
|
LimeLog.warning("JSON error, failed to read computer address information, " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
// External port is persisted in the remote address field
|
// External port is persisted in the remote address field
|
||||||
if (details.remoteAddress != null) {
|
if (details.remoteAddress != null) {
|
||||||
|
@ -0,0 +1,123 @@
|
|||||||
|
package com.limelight.computers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
|
||||||
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LegacyDatabaseReader3 {
|
||||||
|
private static final String COMPUTER_DB_NAME = "computers3.db";
|
||||||
|
private static final String COMPUTER_TABLE_NAME = "Computers";
|
||||||
|
|
||||||
|
private static final char ADDRESS_DELIMITER = ';';
|
||||||
|
private static final char PORT_DELIMITER = '_';
|
||||||
|
|
||||||
|
private static String readNonEmptyString(String input) {
|
||||||
|
if (input.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComputerDetails.AddressTuple splitAddressToTuple(String input) {
|
||||||
|
if (input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = input.split(""+PORT_DELIMITER, -1);
|
||||||
|
if (parts.length == 1) {
|
||||||
|
return new ComputerDetails.AddressTuple(parts[0], NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new ComputerDetails.AddressTuple(parts[0], Integer.parseInt(parts[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String splitTupleToAddress(ComputerDetails.AddressTuple tuple) {
|
||||||
|
return tuple.address+PORT_DELIMITER+tuple.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComputerDetails getComputerFromCursor(Cursor c) {
|
||||||
|
ComputerDetails details = new ComputerDetails();
|
||||||
|
|
||||||
|
details.uuid = c.getString(0);
|
||||||
|
details.name = c.getString(1);
|
||||||
|
|
||||||
|
String[] addresses = c.getString(2).split(""+ADDRESS_DELIMITER, -1);
|
||||||
|
|
||||||
|
details.localAddress = splitAddressToTuple(readNonEmptyString(addresses[0]));
|
||||||
|
details.remoteAddress = splitAddressToTuple(readNonEmptyString(addresses[1]));
|
||||||
|
details.manualAddress = splitAddressToTuple(readNonEmptyString(addresses[2]));
|
||||||
|
details.ipv6Address = splitAddressToTuple(readNonEmptyString(addresses[3]));
|
||||||
|
|
||||||
|
// External port is persisted in the remote address field
|
||||||
|
if (details.remoteAddress != null) {
|
||||||
|
details.externalPort = details.remoteAddress.port;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
details.externalPort = NvHTTP.DEFAULT_HTTP_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
details.macAddress = c.getString(3);
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] derCertData = c.getBlob(4);
|
||||||
|
|
||||||
|
if (derCertData != null) {
|
||||||
|
details.serverCert = (X509Certificate) CertificateFactory.getInstance("X.509")
|
||||||
|
.generateCertificate(new ByteArrayInputStream(derCertData));
|
||||||
|
}
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This signifies we don't have dynamic state (like pair state)
|
||||||
|
details.state = ComputerDetails.State.UNKNOWN;
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ComputerDetails> getAllComputers(SQLiteDatabase computerDb) {
|
||||||
|
try (final Cursor c = computerDb.rawQuery("SELECT * FROM "+COMPUTER_TABLE_NAME, null)) {
|
||||||
|
LinkedList<ComputerDetails> computerList = new LinkedList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
ComputerDetails details = getComputerFromCursor(c);
|
||||||
|
|
||||||
|
// If a critical field is corrupt or missing, skip the database entry
|
||||||
|
if (details.uuid == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
computerList.add(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
return computerList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ComputerDetails> migrateAllComputers(Context c) {
|
||||||
|
try (final SQLiteDatabase computerDb = SQLiteDatabase.openDatabase(
|
||||||
|
c.getDatabasePath(COMPUTER_DB_NAME).getPath(),
|
||||||
|
null, SQLiteDatabase.OPEN_READONLY)
|
||||||
|
) {
|
||||||
|
// Open the existing database
|
||||||
|
return getAllComputers(computerDb);
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
return new LinkedList<ComputerDetails>();
|
||||||
|
} finally {
|
||||||
|
// Close and delete the old DB
|
||||||
|
c.deleteDatabase(COMPUTER_DB_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user