diff --git a/app/src/main/java/org/xbmc/kore/eventclient/EventServerConnection.java b/app/src/main/java/org/xbmc/kore/eventclient/EventServerConnection.java index 44eaf55..71ba9d3 100644 --- a/app/src/main/java/org/xbmc/kore/eventclient/EventServerConnection.java +++ b/app/src/main/java/org/xbmc/kore/eventclient/EventServerConnection.java @@ -193,7 +193,7 @@ public class EventServerConnection { final HostConnection auxHostConnection = new HostConnection( new HostInfo(hostInfo.getName(), hostInfo.getAddress(), HostConnection.PROTOCOL_HTTP, hostInfo.getHttpPort(), hostInfo.getTcpPort(), - hostInfo.getUsername(), hostInfo.getPassword(), false, 0)); + hostInfo.getUsername(), hostInfo.getPassword(), false, 0, hostInfo.isHttps)); final Application.GetProperties action = new Application.GetProperties(Application.GetProperties.MUTED); final Packet mutePacket = new PacketBUTTON(ButtonCodes.MAP_REMOTE, ButtonCodes.REMOTE_MUTE, false, true, true, (short) 0, (byte) 0); diff --git a/app/src/main/java/org/xbmc/kore/host/HostInfo.java b/app/src/main/java/org/xbmc/kore/host/HostInfo.java index 58334dd..3675d43 100644 --- a/app/src/main/java/org/xbmc/kore/host/HostInfo.java +++ b/app/src/main/java/org/xbmc/kore/host/HostInfo.java @@ -30,6 +30,11 @@ public class HostInfo { private static final String JSON_RPC_ENDPOINT = "/jsonrpc"; + /** + * Default HTTPS port + */ + public static final int DEFAULT_HTTPS_PORT = 443; + /** * Default HTTP port for XBMC (80 on Windows, 8080 on others) */ @@ -58,28 +63,29 @@ public class HostInfo { /** * Internal id of the host */ - private int id; + private final int id; /** * Friendly name of the host */ - private String name; + private final String name; /** * Connection information */ - private String address; - private int httpPort; - private int tcpPort; + private final String address; + private final int httpPort; + private final int tcpPort; + public final boolean isHttps; private boolean useEventServer; - private int eventServerPort; + private final int eventServerPort; /** * Authentication information */ - private String username; - private String password; + private final String username; + private final String password; /** * Mac address and Wake On Lan port @@ -104,9 +110,9 @@ public class HostInfo { /** * Last time updated (in millis) */ - private long updated; + private final long updated; - private String auxImageHttpAddress; + private final String auxImageHttpAddress; /** * Full constructor. This constructor should be used when instantiating from the database @@ -124,7 +130,7 @@ public class HostInfo { String username, String password, String macAddress, int wolPort, boolean useEventServer, int eventServerPort, int kodiVersionMajor, int kodiVersionMinor, String kodiVersionRevision, String kodiVersionTag, - long updated) { + long updated, boolean isHttps) { this.id = id; this.name = name; this.address = address; @@ -133,6 +139,7 @@ public class HostInfo { } this.protocol = protocol; this.httpPort = httpPort; + this.isHttps = isHttps; this.tcpPort = tcpPort; this.username = username; this.password = password; @@ -165,12 +172,12 @@ public class HostInfo { */ public HostInfo(String name, String address, int protocol, int httpPort, int tcpPort, String username, String password, - boolean useEventServer, int eventServerPort) { + boolean useEventServer, int eventServerPort, boolean isHttps) { this(-1, name, address, protocol, httpPort, tcpPort, username, password, null, DEFAULT_WOL_PORT, useEventServer, eventServerPort, DEFAULT_KODI_VERSION_MAJOR, DEFAULT_KODI_VERSION_MINOR, DEFAULT_KODI_VERSION_REVISION, DEFAULT_KODI_VERSION_TAG, - 0); + 0, isHttps); } public int getId() { @@ -289,7 +296,8 @@ public class HostInfo { * @return HTTP URL eg. http://192.168.1.1:8080 */ public String getHttpURL() { - return "http://" + address + ":" + httpPort; + String scheme = isHttps ? "https://" : "http://"; + return scheme + address + ":" + httpPort; } /** @@ -297,7 +305,7 @@ public class HostInfo { * @return HTTP URL eg. http://192.168.1.1:8080/jsonrpc */ public String getJsonRpcHttpEndpoint() { - return "http://" + address + ":" + httpPort + JSON_RPC_ENDPOINT; + return getHttpURL() + JSON_RPC_ENDPOINT; } /** diff --git a/app/src/main/java/org/xbmc/kore/host/HostManager.java b/app/src/main/java/org/xbmc/kore/host/HostManager.java index 9086f62..e79e78c 100644 --- a/app/src/main/java/org/xbmc/kore/host/HostManager.java +++ b/app/src/main/java/org/xbmc/kore/host/HostManager.java @@ -155,12 +155,13 @@ public class HostManager { int kodiVersionMinor = cursor.getInt(idx++); String kodiVersionRevision = cursor.getString(idx++); String kodiVersionTag = cursor.getString(idx++); + boolean isHttps = (cursor.getInt(idx++) != 0); hosts.add(new HostInfo( id, name, address, protocol, httpPort, tcpPort, username, password, macAddress, wolPort, useEventServer, eventServerPort, kodiVersionMajor, kodiVersionMinor, kodiVersionRevision, kodiVersionTag, - updated)); + updated, isHttps)); } } cursor.close(); @@ -328,7 +329,8 @@ public class HostManager { hostInfo.getMacAddress(), hostInfo.getWolPort(), hostInfo.getUseEventServer(), hostInfo.getEventServerPort(), hostInfo.getKodiVersionMajor(), hostInfo.getKodiVersionMinor(), - hostInfo.getKodiVersionRevision(), hostInfo.getKodiVersionTag()); + hostInfo.getKodiVersionRevision(), hostInfo.getKodiVersionTag(), + hostInfo.isHttps); } /** @@ -345,7 +347,8 @@ public class HostManager { public HostInfo addHost(String name, String address, int protocol, int httpPort, int tcpPort, String username, String password, String macAddress, int wolPort, boolean useEventServer, int eventServerPort, - int kodiVersionMajor, int kodiVersionMinor, String kodiVersionRevision, String kodiVersionTag) { + int kodiVersionMajor, int kodiVersionMinor, String kodiVersionRevision, String kodiVersionTag, + boolean isHttps) { ContentValues values = new ContentValues(); values.put(MediaContract.HostsColumns.NAME, name); @@ -359,11 +362,12 @@ public class HostManager { values.put(MediaContract.HostsColumns.WOL_PORT, wolPort); values.put(MediaContract.HostsColumns.USE_EVENT_SERVER, useEventServer); values.put(MediaContract.HostsColumns.EVENT_SERVER_PORT, eventServerPort); - values.put(MediaContract.HostsColumns.KODI_VERSION_MAJOR, kodiVersionMajor); values.put(MediaContract.HostsColumns.KODI_VERSION_MINOR, kodiVersionMinor); values.put(MediaContract.HostsColumns.KODI_VERSION_REVISION, kodiVersionRevision); values.put(MediaContract.HostsColumns.KODI_VERSION_TAG, kodiVersionTag); + values.put(MediaContract.HostsColumns.IS_HTTPS, isHttps); + Uri newUri = context.getContentResolver() .insert(MediaContract.Hosts.CONTENT_URI, values); @@ -400,11 +404,11 @@ public class HostManager { values.put(MediaContract.HostsColumns.WOL_PORT, newHostInfo.getWolPort()); values.put(MediaContract.HostsColumns.USE_EVENT_SERVER, newHostInfo.getUseEventServer()); values.put(MediaContract.HostsColumns.EVENT_SERVER_PORT, newHostInfo.getEventServerPort()); - values.put(MediaContract.HostsColumns.KODI_VERSION_MAJOR, newHostInfo.getKodiVersionMajor()); values.put(MediaContract.HostsColumns.KODI_VERSION_MINOR, newHostInfo.getKodiVersionMinor()); values.put(MediaContract.HostsColumns.KODI_VERSION_REVISION, newHostInfo.getKodiVersionRevision()); values.put(MediaContract.HostsColumns.KODI_VERSION_TAG, newHostInfo.getKodiVersionTag()); + values.put(MediaContract.HostsColumns.IS_HTTPS, newHostInfo.isHttps); context.getContentResolver() .update(MediaContract.Hosts.buildHostUri(hostId), values, null, null); diff --git a/app/src/main/java/org/xbmc/kore/jsonrpc/HostConnection.java b/app/src/main/java/org/xbmc/kore/jsonrpc/HostConnection.java index a34ebe6..4a47a35 100644 --- a/app/src/main/java/org/xbmc/kore/jsonrpc/HostConnection.java +++ b/app/src/main/java/org/xbmc/kore/jsonrpc/HostConnection.java @@ -308,141 +308,8 @@ public class HostConnection { }; executorService.execute(command); - //new Thread(command).start(); } -// /** -// * Sends the JSON RPC request through HTTP -// */ -// private void executeThroughHttp(final ApiMethod method, final ApiCallback callback, -// final Handler handler) { -// String jsonRequest = method.toJsonString(); -// try { -// HttpURLConnection connection = openHttpConnection(hostInfo); -// sendHttpRequest(connection, jsonRequest); -// // Read response and convert it -// final T result = method.resultFromJson(parseJsonResponse(readHttpResponse(connection))); -// -// if ((handler != null) && (callback != null)) { -// handler.post(new Runnable() { -// @Override -// public void run() { -// callback.onSuccess(result); -// } -// }); -// } -// } catch (final ApiException e) { -// // Got an error, call error handler -// -// if ((handler != null) && (callback != null)) { -// handler.post(new Runnable() { -// @Override -// public void run() { -// callback.onError(e.getCode(), e.getMessage()); -// } -// }); -// } -// } -// } -// -// /** -// * Auxiliary method to open a HTTP connection. -// * This method calls connect() so that any errors are cathced -// * @param hostInfo Host info -// * @return Connection set up -// * @throws ApiException -// */ -// private HttpURLConnection openHttpConnection(HostInfo hostInfo) throws ApiException { -// try { -//// LogUtils.LOGD(TAG, "Opening HTTP connection."); -// HttpURLConnection connection = (HttpURLConnection) new URL(hostInfo.getJsonRpcHttpEndpoint()).openConnection(); -// connection.setRequestMethod("POST"); -// connection.setConnectTimeout(connectTimeout); -// //connection.setReadTimeout(connectTimeout); -// connection.setRequestProperty("Content-Type", "application/json"); -// connection.setDoOutput(true); -// -// // http basic authorization -// if ((hostInfo.getUsername() != null) && !hostInfo.getUsername().isEmpty() && -// (hostInfo.getPassword() != null) && !hostInfo.getPassword().isEmpty()) { -// final String token = Base64.encodeToString((hostInfo.getUsername() + ":" + -// hostInfo.getPassword()).getBytes(), Base64.DEFAULT); -// connection.setRequestProperty("Authorization", "Basic " + token); -// } -// -// // Check the connection -// connection.connect(); -// return connection; -// } catch (ProtocolException e) { -// // Won't try to catch this -// LogUtils.LOGE(TAG, "Got protocol exception while opening HTTP connection.", e); -// throw new RuntimeException(e); -// } catch (IOException e) { -// LogUtils.LOGW(TAG, "Failed to open HTTP connection.", e); -// throw new ApiException(ApiException.IO_EXCEPTION_WHILE_CONNECTING, e); -// } -// } -// -// /** -// * Send an HTTP POST request -// * @param connection Open connection -// * @param request Request to send -// * @throws ApiException -// */ -// private void sendHttpRequest(HttpURLConnection connection, String request) throws ApiException { -// try { -// LogUtils.LOGD(TAG, "Sending request via HTTP: " + request); -// OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); -// out.write(request); -// out.flush(); -// out.close(); -// } catch (IOException e) { -// LogUtils.LOGW(TAG, "Failed to send HTTP request.", e); -// throw new ApiException(ApiException.IO_EXCEPTION_WHILE_SENDING_REQUEST, e); -// } -// } -// -// /** -// * Reads the response from the server -// * @param connection Connection -// * @return Response read -// * @throws ApiException -// */ -// private String readHttpResponse(HttpURLConnection connection) throws ApiException { -// try { -//// LogUtils.LOGD(TAG, "Reading HTTP response."); -// int responseCode = connection.getResponseCode(); -// -// switch (responseCode) { -// case 200: -// // All ok, read response -// BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); -// StringBuilder response = new StringBuilder(); -// String inputLine; -// while ((inputLine = in.readLine()) != null) -// response.append(inputLine); -// in.close(); -// LogUtils.LOGD(TAG, "HTTP response: " + response.toString()); -// return response.toString(); -// case 401: -// LogUtils.LOGD(TAG, "HTTP response read error. Got a 401."); -// throw new ApiException(ApiException.HTTP_RESPONSE_CODE_UNAUTHORIZED, -// "Server returned response code: " + responseCode); -// case 404: -// LogUtils.LOGD(TAG, "HTTP response read error. Got a 404."); -// throw new ApiException(ApiException.HTTP_RESPONSE_CODE_NOT_FOUND, -// "Server returned response code: " + responseCode); -// default: -// LogUtils.LOGD(TAG, "HTTP response read error. Got: " + responseCode); -// throw new ApiException(ApiException.HTTP_RESPONSE_CODE_UNKNOWN, -// "Server returned response code: " + responseCode); -// } -// } catch (IOException e) { -// LogUtils.LOGW(TAG, "Failed to read HTTP response.", e); -// throw new ApiException(ApiException.IO_EXCEPTION_WHILE_READING_RESPONSE, e); -// } -// } - /** * Sends the JSON RPC request through HTTP (using OkHttp library) */ diff --git a/app/src/main/java/org/xbmc/kore/provider/MediaContract.java b/app/src/main/java/org/xbmc/kore/provider/MediaContract.java index 5c732f5..3b544fd 100644 --- a/app/src/main/java/org/xbmc/kore/provider/MediaContract.java +++ b/app/src/main/java/org/xbmc/kore/provider/MediaContract.java @@ -76,6 +76,7 @@ public class MediaContract { String KODI_VERSION_MINOR = "kodi_version_minor"; String KODI_VERSION_REVISION = "kodi_version_revision"; String KODI_VERSION_TAG = "kodi_version_tag"; + String IS_HTTPS = "is_https"; } public static class Hosts implements BaseColumns, SyncColumns, HostsColumns { @@ -100,7 +101,8 @@ public class MediaContract { public final static String[] ALL_COLUMNS = { _ID, UPDATED, NAME, ADDRESS, PROTOCOL, HTTP_PORT, TCP_PORT, USERNAME, PASSWORD, MAC_ADDRESS, WOL_PORT, USE_EVENT_SERVER, EVENT_SERVER_PORT, - KODI_VERSION_MAJOR, KODI_VERSION_MINOR, KODI_VERSION_REVISION, KODI_VERSION_TAG + KODI_VERSION_MAJOR, KODI_VERSION_MINOR, KODI_VERSION_REVISION, KODI_VERSION_TAG, + IS_HTTPS }; } diff --git a/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java b/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java index 2e1ac33..51453b0 100644 --- a/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java +++ b/app/src/main/java/org/xbmc/kore/provider/MediaDatabase.java @@ -36,7 +36,8 @@ public class MediaDatabase extends SQLiteOpenHelper { DB_VERSION_PRE_SONG_DISPLAY_ARTIST = 6, DB_VERSION_PRE_SONG_DISC = 7, DB_VERSION_PRE_HOST_VERSION = 8, - DB_VERSION = 9; + DB_VERSION_PRE_HOST_HTTPS = 9, + DB_VERSION = 10; /** * Tables exposed @@ -153,11 +154,11 @@ public class MediaDatabase extends SQLiteOpenHelper { MediaContract.HostsColumns.WOL_PORT + " INTEGER, " + MediaContract.HostsColumns.USE_EVENT_SERVER + " INTEGER, " + MediaContract.HostsColumns.EVENT_SERVER_PORT + " INTEGER, " + - MediaContract.HostsColumns.KODI_VERSION_MAJOR + " INTEGER, " + MediaContract.HostsColumns.KODI_VERSION_MINOR + " INTEGER, " + MediaContract.HostsColumns.KODI_VERSION_REVISION + " TEXT, " + - MediaContract.HostsColumns.KODI_VERSION_TAG + " TEXT)" + MediaContract.HostsColumns.KODI_VERSION_TAG + " TEXT, " + + MediaContract.HostsColumns.IS_HTTPS + " INTEGER)" ); // Movies @@ -515,6 +516,10 @@ public class MediaDatabase extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Tables.HOSTS + " ADD COLUMN " + MediaContract.HostsColumns.KODI_VERSION_TAG + " TEXT DEFAULT " + HostInfo.DEFAULT_KODI_VERSION_TAG + ";"); + case DB_VERSION_PRE_HOST_HTTPS: + db.execSQL("ALTER TABLE " + Tables.HOSTS + + " ADD COLUMN " + MediaContract.HostsColumns.IS_HTTPS + + " INTEGER DEFAULT 0;"); } } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/hosts/AddHostFragmentZeroconf.java b/app/src/main/java/org/xbmc/kore/ui/sections/hosts/AddHostFragmentZeroconf.java index 4f43e6b..0d993c4 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/hosts/AddHostFragmentZeroconf.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/hosts/AddHostFragmentZeroconf.java @@ -283,7 +283,7 @@ public class AddHostFragmentZeroconf extends Fragment { String hostAddress = addresses[0]; int hostHttpPort = selectedServiceInfo.getPort(); HostInfo selectedHostInfo = new HostInfo(hostName, hostAddress, HostConnection.PROTOCOL_TCP, - hostHttpPort, HostInfo.DEFAULT_TCP_PORT, null, null, true, HostInfo.DEFAULT_EVENT_SERVER_PORT); + hostHttpPort, HostInfo.DEFAULT_TCP_PORT, null, null, true, HostInfo.DEFAULT_EVENT_SERVER_PORT, false); listener.onAddHostZeroconfFoundHost(selectedHostInfo); } diff --git a/app/src/main/java/org/xbmc/kore/ui/sections/hosts/HostFragmentManualConfiguration.java b/app/src/main/java/org/xbmc/kore/ui/sections/hosts/HostFragmentManualConfiguration.java index 69ad956..acde208 100644 --- a/app/src/main/java/org/xbmc/kore/ui/sections/hosts/HostFragmentManualConfiguration.java +++ b/app/src/main/java/org/xbmc/kore/ui/sections/hosts/HostFragmentManualConfiguration.java @@ -43,6 +43,9 @@ import org.xbmc.kore.jsonrpc.type.ApplicationType; import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.NetUtils; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import butterknife.ButterKnife; import butterknife.InjectView; @@ -212,6 +215,10 @@ public class HostFragmentManualConfiguration extends Fragment { } } + private static boolean isValidPort(int port) { + return port > 0 && port <= 65535; + } + /** * Tests a connection with the values set in the UI. * Checks whether the values are correctly set, and then tries to make @@ -220,14 +227,60 @@ public class HostFragmentManualConfiguration extends Fragment { */ private void testConnection() { String xbmcName = xbmcNameEditText.getText().toString(); - String xbmcAddress = xbmcIpAddressEditText.getText().toString(); - int xbmcHttpPort; + boolean isHttps = false; + String xbmcAddress = xbmcIpAddressEditText.getText().toString(); + if (xbmcAddress.startsWith("https://")) { + xbmcAddress = xbmcAddress.substring("https://".length()); + LogUtils.LOGD(TAG, "Stripped https:// on address to get: " + xbmcAddress); + isHttps = true; + } else if (xbmcAddress.startsWith("http://")) { + xbmcAddress = xbmcAddress.substring("http://".length()); + LogUtils.LOGD(TAG, "Stripped http:// on address to get: " + xbmcAddress); + } + int xbmcHttpPort = isHttps ? HostInfo.DEFAULT_HTTPS_PORT : HostInfo.DEFAULT_HTTP_PORT; + + Integer implicitPort = null; + Matcher m = Pattern.compile("^.*:(\\d{1,5})\\z").matcher(xbmcAddress); + if (m.matches()) { + // Minus one character for the colon + xbmcAddress = xbmcAddress.substring(0, m.start(1) - 1); + LogUtils.LOGD(TAG, "Stripped port on address to get: " + xbmcAddress); + try { + implicitPort = Integer.valueOf(m.group(1)); + } catch (NumberFormatException e) { + LogUtils.LOGW( + TAG, + "Value matching port regex couldn't be parsed as integer: " + m.group(1) + ); + implicitPort = -1; + } + } + + Integer explicitPort = null; String aux = xbmcHttpPortEditText.getText().toString(); - try { - xbmcHttpPort = TextUtils.isEmpty(aux) ? HostInfo.DEFAULT_HTTP_PORT : Integer.valueOf(aux); - } catch (NumberFormatException exc) { - xbmcHttpPort = -1; + if (!TextUtils.isEmpty(aux)) { + try { + explicitPort = Integer.valueOf(aux); + } catch (NumberFormatException e) { + explicitPort = -1; + } + } + + if (implicitPort != null) { + if (!isValidPort(implicitPort)) { + Toast.makeText(getActivity(), R.string.wizard_invalid_http_port_specified, Toast.LENGTH_SHORT); + xbmcIpAddressEditText.requestFocus(); + return; + } + xbmcHttpPort = implicitPort; + } else if (explicitPort != null) { + if (!isValidPort(explicitPort)) { + Toast.makeText(getActivity(), R.string.wizard_invalid_http_port_specified, Toast.LENGTH_SHORT); + xbmcHttpPortEditText.requestFocus(); + return; + } + xbmcHttpPort = explicitPort; } String xbmcUsername = xbmcUsernameEditText.getText().toString(); @@ -270,10 +323,6 @@ public class HostFragmentManualConfiguration extends Fragment { Toast.makeText(getActivity(), R.string.wizard_no_address_specified, Toast.LENGTH_SHORT).show(); xbmcIpAddressEditText.requestFocus(); return; - } else if (xbmcHttpPort <= 0) { - Toast.makeText(getActivity(), R.string.wizard_invalid_http_port_specified, Toast.LENGTH_SHORT).show(); - xbmcHttpPortEditText.requestFocus(); - return; } else if (xbmcTcpPort <= 0) { Toast.makeText(getActivity(), R.string.wizard_invalid_tcp_port_specified, Toast.LENGTH_SHORT).show(); xbmcTcpPortEditText.requestFocus(); @@ -294,7 +343,7 @@ public class HostFragmentManualConfiguration extends Fragment { final HostInfo checkedHostInfo = new HostInfo(xbmcName, xbmcAddress, xbmcProtocol, xbmcHttpPort, xbmcTcpPort, xbmcUsername, xbmcPassword, - xbmcUseEventServer, xbmcEventServerPort); + xbmcUseEventServer, xbmcEventServerPort, isHttps); checkedHostInfo.setMacAddress(macAddress); checkedHostInfo.setWolPort(xbmcWolPort); diff --git a/app/src/testUtils/java/org/xbmc/kore/testutils/Database.java b/app/src/testUtils/java/org/xbmc/kore/testutils/Database.java index 900990c..86a290f 100644 --- a/app/src/testUtils/java/org/xbmc/kore/testutils/Database.java +++ b/app/src/testUtils/java/org/xbmc/kore/testutils/Database.java @@ -62,7 +62,8 @@ public class Database { return HostManager.getInstance(context).addHost("TestHost", "127.0.0.1", 1, 80, 9090, null, null, "52:54:00:12:35:02", 9, false, 9777, HostInfo.DEFAULT_KODI_VERSION_MAJOR, HostInfo.DEFAULT_KODI_VERSION_MINOR, - HostInfo.DEFAULT_KODI_VERSION_REVISION, HostInfo.DEFAULT_KODI_VERSION_TAG); + HostInfo.DEFAULT_KODI_VERSION_REVISION, HostInfo.DEFAULT_KODI_VERSION_TAG, + false); } public static void insertMovies(Context context, ContentResolver contentResolver, int hostId)