diff --git a/bundles/org.openhab.binding.magentatv/README.md b/bundles/org.openhab.binding.magentatv/README.md index 02acd4a1b12..ddfcf610400 100644 --- a/bundles/org.openhab.binding.magentatv/README.md +++ b/bundles/org.openhab.binding.magentatv/README.md @@ -117,7 +117,7 @@ For security reasons the credentials are automatically deleted from the thing co | |key |String |Send key code to the receiver (see code table below) | | |mute |Switch |Mute volume (mute the speaker) | |status |playMode |String |Current play mode - this info is not reliable | -| |channelCode |Number |The channel code from the EPG. | +| |channelCode |Number |The channel code from the EPG. | |program |title |String |Title of the running program or video being played | | |text |String |Some description (as reported by the receiver, could be empty) | | |start |DateTime |Time when the program started | @@ -327,4 +327,4 @@ to switch it ON and to switch it off. -After an openHAB restart you need to make sure that OH and receiver are in sync, because the binding can't read the power status at startup. +After an openHAB restart you need to make sure that OH and receiver are in sync, because the binding can't read the power status at startup. \ No newline at end of file diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVBindingConstants.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVBindingConstants.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java index 6f041ddb7c8..7c62e31b156 100644 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVConsoleHandler.java @@ -20,18 +20,15 @@ import java.io.InputStreamReader; import java.util.Arrays; import java.util.List; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.magentatv.internal.network.MagentaTVOAuth; import org.openhab.core.io.console.Console; import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension; import org.openhab.core.io.console.extensions.ConsoleCommandExtension; +import org.openhab.core.io.net.http.HttpClientFactory; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,14 +44,12 @@ public class MagentaTVConsoleHandler extends AbstractConsoleCommandExtension { private static final String CMD_LOGIN = "login"; private final Logger logger = LoggerFactory.getLogger(MagentaTVConsoleHandler.class); - private final MagentaTVOAuth oauth = new MagentaTVOAuth(); - - @Reference(cardinality = ReferenceCardinality.OPTIONAL) - private @Nullable ClientBuilder injectedClientBuilder; + private final MagentaTVOAuth oauth; @Activate - public MagentaTVConsoleHandler() { + public MagentaTVConsoleHandler(@Reference HttpClientFactory httpClientFactory) { super(BINDING_ID, "Interact with the " + BINDING_ID + " integration."); + oauth = new MagentaTVOAuth(httpClientFactory.getCommonHttpClient()); } @Override diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVException.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVException.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVGsonDTO.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVGsonDTO.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java old mode 100644 new mode 100755 index 87484939660..7508121483e --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/MagentaTVHandlerFactory.java @@ -19,10 +19,12 @@ import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager.MagentaTVDevice; import org.openhab.binding.magentatv.internal.handler.MagentaTVHandler; import org.openhab.binding.magentatv.internal.network.MagentaTVNetwork; import org.openhab.binding.magentatv.internal.network.MagentaTVPoweroffListener; +import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.net.HttpServiceUtil; import org.openhab.core.net.NetworkAddressService; import org.openhab.core.thing.Thing; @@ -51,6 +53,7 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory { private final MagentaTVNetwork network = new MagentaTVNetwork(); private final MagentaTVDeviceManager manager; + private final HttpClient httpClient; private @Nullable MagentaTVPoweroffListener upnpListener; private boolean servletInitialized = false; @@ -64,11 +67,11 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory { @Activate public MagentaTVHandlerFactory(@Reference NetworkAddressService networkAddressService, - @Reference MagentaTVDeviceManager manager, ComponentContext componentContext, - Map<String, String> configProperties) throws IOException { + @Reference HttpClientFactory httpClientFactory, @Reference MagentaTVDeviceManager manager, + ComponentContext componentContext, Map<String, String> configProperties) throws IOException { super.activate(componentContext); this.manager = manager; - + this.httpClient = httpClientFactory.getCommonHttpClient(); try { logger.debug("Initialize network access"); System.setProperty("java.net.preferIPv4Stack", "true"); @@ -99,7 +102,7 @@ public class MagentaTVHandlerFactory extends BaseThingHandlerFactory { logger.debug("Create thing type {}", thing.getThingTypeUID().getAsString()); if (THING_TYPE_RECEIVER.equals(thingTypeUID)) { - return new MagentaTVHandler(manager, thing, network); + return new MagentaTVHandler(manager, thing, network, httpClient); } return null; diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/config/MagentaTVThingConfiguration.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/config/MagentaTVThingConfiguration.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/discovery/MagentaTVDiscoveryParticipant.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/discovery/MagentaTVDiscoveryParticipant.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java old mode 100644 new mode 100755 index f67ff2b4fa8..68bed02f442 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVControl.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.StringTokenizer; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.config.MagentaTVDynamicConfig; import org.openhab.binding.magentatv.internal.network.MagentaTVHttp; @@ -44,7 +45,7 @@ public class MagentaTVControl { private final MagentaTVNetwork network; private final MagentaTVHttp http = new MagentaTVHttp(); - private final MagentaTVOAuth oauth = new MagentaTVOAuth(); + private final MagentaTVOAuth oauth; private final MagentaTVDynamicConfig config; private boolean initialized = false; private String thingId = ""; @@ -52,11 +53,13 @@ public class MagentaTVControl { public MagentaTVControl() { config = new MagentaTVDynamicConfig(); network = new MagentaTVNetwork(); + oauth = new MagentaTVOAuth(new HttpClient()); } - public MagentaTVControl(MagentaTVDynamicConfig config, MagentaTVNetwork network) { + public MagentaTVControl(MagentaTVDynamicConfig config, MagentaTVNetwork network, HttpClient httpClient) { thingId = config.getFriendlyName(); this.network = network; + this.oauth = new MagentaTVOAuth(httpClient); this.config = config; this.config.setTerminalID(computeMD5(network.getLocalMAC().toUpperCase() + config.getUDN())); this.config.setLocalIP(network.getLocalIP()); @@ -391,7 +394,8 @@ public class MagentaTVControl { // direct key code return key; } - return KEY_MAP.getOrDefault(key, ""); + String code = KEY_MAP.get(key); + return code != null ? code : ""; } /** diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java old mode 100644 new mode 100755 index 66efbe059c7..3d9a9f67169 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVHandler.java @@ -33,6 +33,7 @@ import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.magentatv.internal.MagentaTVDeviceManager; import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.MRPayEvent; @@ -88,6 +89,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe private final Gson gson; protected final MagentaTVNetwork network; protected final MagentaTVDeviceManager manager; + private final HttpClient httpClient; protected MagentaTVControl control = new MagentaTVControl(); private String thingId = ""; @@ -102,10 +104,12 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe * @param thing * @param bindingConfig */ - public MagentaTVHandler(MagentaTVDeviceManager manager, Thing thing, MagentaTVNetwork network) { + public MagentaTVHandler(MagentaTVDeviceManager manager, Thing thing, MagentaTVNetwork network, + HttpClient httpClient) { super(thing); this.manager = manager; this.network = network; + this.httpClient = httpClient; gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new MRProgramInfoEventInstanceCreator()) .registerTypeAdapter(OAuthTokenResponse.class, new MRProgramStatusInstanceCreator()) .registerTypeAdapter(OAuthAuthenticateResponse.class, new MRShortProgramInfoInstanceCreator()) @@ -150,7 +154,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe } config.setMacAddress(macAddress); } - control = new MagentaTVControl(config, network); + control = new MagentaTVControl(config, network, httpClient); config.updateNetwork(control.getConfig()); // get network parameters from control // Check for emoty credentials (e.g. missing in .things file) @@ -185,7 +189,7 @@ public class MagentaTVHandler extends BaseThingHandler implements MagentaTVListe } /** - * This routine is called every time the Thing configuration has been changed. + * This routine is called every time the Thing configuration has been changed */ @Override public void handleConfigurationUpdate(Map<String, Object> configurationParameters) { diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVListener.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/handler/MagentaTVListener.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVHttp.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVHttp.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java old mode 100644 new mode 100755 index 0a2ab367fb8..c2554689312 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNetwork.java @@ -20,7 +20,6 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; -import org.apache.commons.net.util.SubnetUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.magentatv.internal.MagentaTVException; @@ -76,34 +75,6 @@ public class MagentaTVNetwork { "Unable to get local IP / MAC address, check network settings in openHAB system configuration!"); } - /** - * Checks if client ip equals or is in range of ip networks provided by - * semicolon separated list - * - * @param clientIp in numeric form like "192.168.0.10" - * @param ipList like "127.0.0.1;192.168.0.0/24;10.0.0.0/8" - * @return true if client ip from the list os ips and networks - */ - public static boolean isIpInSubnet(String clientIp, String ipList) { - if (ipList.isEmpty()) { - // No ip address provided - return true; - } - String[] subnetMasks = ipList.split(";"); - for (String subnetMask : subnetMasks) { - subnetMask = subnetMask.trim(); - if (clientIp.equals(subnetMask)) { - return true; - } - if (subnetMask.contains("/")) { - if (new SubnetUtils(subnetMask).getInfo().isInRange(clientIp)) { - return true; - } - } - } - return false; - } - @Nullable public NetworkInterface getLocalInterface() { return localInterface; diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNotifyServlet.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVNotifyServlet.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java old mode 100644 new mode 100755 index 36acdb4ebc2..a6b6539ba62 --- a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java +++ b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVOAuth.java @@ -13,21 +13,34 @@ package org.openhab.binding.magentatv.internal.network; import static org.openhab.binding.magentatv.internal.MagentaTVBindingConstants.*; -import static org.openhab.binding.magentatv.internal.MagentaTVUtil.substringAfterLast; +import static org.openhab.binding.magentatv.internal.MagentaTVUtil.*; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpCookie; import java.net.URLEncoder; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; import java.util.Properties; import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.ws.rs.HttpMethod; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.magentatv.internal.MagentaTVException; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponse; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OAuthAuthenticateResponseInstanceCreator; @@ -37,7 +50,6 @@ import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentials; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthCredentialsInstanceCreator; import org.openhab.binding.magentatv.internal.MagentaTVGsonDTO.OauthKeyValue; import org.openhab.binding.magentatv.internal.handler.MagentaTVControl; -import org.openhab.core.io.net.http.HttpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +62,7 @@ import com.google.gson.GsonBuilder; * * @author Markus Michels - Initial contribution * - * Deutsche Telekom uses a OAuth-based authentication to access the EPG portal. The + * Deutsche Telekom uses an OAuth-based authentication to access the EPG portal. The * communication between the MR and the remote app requires a pairing before the receiver could be * controlled by sending keys etc. The so called userID is not directly derived from any local parameters * (like terminalID as a has from the mac address), but will be returned as a result from the OAuth @@ -63,9 +75,12 @@ import com.google.gson.GsonBuilder; @NonNullByDefault public class MagentaTVOAuth { private final Logger logger = LoggerFactory.getLogger(MagentaTVOAuth.class); - final Gson gson; + private HttpClient httpClient; + private final Gson gson; + private List<HttpCookie> cookies = new ArrayList<>(); - public MagentaTVOAuth() { + public MagentaTVOAuth(HttpClient httpClient) { + this.httpClient = httpClient; gson = new GsonBuilder().registerTypeAdapter(OauthCredentials.class, new OauthCredentialsInstanceCreator()) .registerTypeAdapter(OAuthTokenResponse.class, new OAuthTokenResponseInstanceCreator()) .registerTypeAdapter(OAuthAuthenticateResponse.class, new OAuthAuthenticateResponseInstanceCreator()) @@ -78,124 +93,209 @@ public class MagentaTVOAuth { throw new MagentaTVException("Credentials for OAuth missing, check thing config!"); } - String step = "initialize"; String url = ""; - Properties httpHeader; + Properties httpHeader = initHttpHeader(); String postData = ""; String httpResponse = ""; - InputStream dataStream = null; // OAuth autentication results String oAuthScope = ""; String oAuthService = ""; String epghttpsurl = ""; - String retcode = ""; - String retmsg = ""; + // Get credentials + url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI; + httpHeader.setProperty(HttpHeader.HOST.toString(), substringAfterLast(OAUTH_GET_CRED_URL, "/")); + httpResponse = httpRequest(HttpMethod.GET, url, httpHeader, ""); + OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class); + epghttpsurl = getString(cred.epghttpsurl); + if (epghttpsurl.isEmpty()) { + throw new MagentaTVException("Unable to determine EPG url"); + } + if (!epghttpsurl.contains("/EPG")) { + epghttpsurl = epghttpsurl + "/EPG"; + } + logger.debug("OAuth: epghttpsurl = {}", epghttpsurl); + + // get OAuth data from response + if (cred.sam3Para != null) { + for (OauthKeyValue si : cred.sam3Para) { + logger.trace("sam3Para.{} = {}", si.key, si.value); + if (si.key.equalsIgnoreCase("oAuthScope")) { + oAuthScope = si.value; + } else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) { + oAuthService = si.value; + } + } + } + if (oAuthScope.isEmpty() || oAuthService.isEmpty()) { + throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse); + } + + // Get OAuth token (New flow based on WebTV) + String userId = ""; + String terminalId = UUID.randomUUID().toString(); + String cnonce = MagentaTVControl.computeMD5(terminalId); + + url = oAuthService + "/oauth2/tokens"; + postData = MessageFormat.format( + "password={0}&scope={1}+offline_access&grant_type=password&username={2}&x_telekom.access_token.format=CompactToken&x_telekom.access_token.encoding=text%2Fbase64&client_id=10LIVESAM30000004901NGTVWEB0000000000000", + urlEncode(accountPassword), oAuthScope, urlEncode(accountName)); + url = oAuthService + "/oauth2/tokens"; + httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData); + OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class); + if (resp.accessToken.isEmpty()) { + String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})", + accountName, getString(resp.errorDescription), getString(resp.error)); + logger.warn("{}", errorMessage); + throw new MagentaTVException(errorMessage); + } + logger.debug("OAuth: Access Token retrieved"); + + // General authentication + logger.debug("OAuth: Generating CSRF token"); + url = "https://api.prod.sngtv.magentatv.de/EPG/JSON/Authenticate"; + httpHeader = initHttpHeader(); + httpHeader.setProperty(HttpHeader.HOST.toString(), "api.prod.sngtv.magentatv.de"); + httpHeader.setProperty("Origin", "https://web.magentatv.de"); + httpHeader.setProperty(HttpHeader.REFERER.toString(), "https://web.magentatv.de/"); + postData = "{\"areaid\":\"1\",\"cnonce\":\"" + cnonce + "\",\"mac\":\"" + terminalId + + "\",\"preSharedKeyID\":\"NGTV000001\",\"subnetId\":\"4901\",\"templatename\":\"NGTV\",\"terminalid\":\"" + + terminalId + + "\",\"terminaltype\":\"WEB-MTV\",\"terminalvendor\":\"WebTV\",\"timezone\":\"Europe/Berlin\",\"usergroup\":\"-1\",\"userType\":3,\"utcEnable\":1}"; + httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData); + String csrf = ""; + for (HttpCookie c : cookies) { // get CRSF Token + String value = c.getValue(); + if (value.contains("CSRFSESSION")) { + csrf = substringBetween(value, "CSRFSESSION" + "=", ";"); + } + } + if (csrf.isEmpty()) { + throw new MagentaTVException("OAuth: Unable to get CSRF token!"); + } + + // Final step: Retrieve userId + url = "https://api.prod.sngtv.magentatv.de/EPG/JSON/DTAuthenticate"; + httpHeader = initHttpHeader(); + httpHeader.setProperty(HttpHeader.HOST.toString(), "api.prod.sngtv.magentatv.de"); + httpHeader.setProperty("Origin", "https://web.magentatv.de"); + httpHeader.setProperty(HttpHeader.REFERER.toString(), "https://web.magentatv.de/"); + httpHeader.setProperty("X_CSRFToken", csrf); + postData = "{\"areaid\":\"1\",\"cnonce\":\"" + cnonce + "\",\"mac\":\"" + terminalId + "\"," + + "\"preSharedKeyID\":\"NGTV000001\",\"subnetId\":\"4901\",\"templatename\":\"NGTV\"," + + "\"terminalid\":\"" + terminalId + "\",\"terminaltype\":\"WEB-MTV\",\"terminalvendor\":\"WebTV\"," + + "\"timezone\":\"Europe/Berlin\",\"usergroup\":\"\",\"userType\":\"1\",\"utcEnable\":1," + + "\"accessToken\":\"" + resp.accessToken + + "\",\"caDeviceInfo\":[{\"caDeviceId\":\"4ef4d933-9a43-41d3-9e3a-84979f22c9eb\"," + + "\"caDeviceType\":8}],\"connectType\":1,\"osversion\":\"Mac OS 10.15.7\",\"softwareVersion\":\"1.33.4.3\"," + + "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + terminalId + "\"}," + + "{\"key\":\"HardwareSupplier\",\"value\":\"WEB-MTV\"},{\"key\":\"DeviceClass\",\"value\":\"TV\"}," + + "{\"key\":\"DeviceStorage\",\"value\":0},{\"key\":\"DeviceStorageSize\",\"value\":0}]}"; + httpResponse = httpRequest(HttpMethod.POST, url, httpHeader, postData); + OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class); + if (authResp.userID.isEmpty()) { + String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}", + accountName, getString(authResp.retcode), getString(authResp.desc)); + logger.warn("{}", errorMessage); + throw new MagentaTVException(errorMessage); + } + userId = getString(authResp.userID); + if (userId.isEmpty()) { + throw new MagentaTVException("No userID received!"); + } + String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase(); + logger.trace("done, userID = {}", hashedUserID); + return hashedUserID; + } + + private String httpRequest(String method, String url, Properties headers, String data) throws MagentaTVException { + String result = ""; try { - step = "get credentials"; - httpHeader = initHttpHeader(); - url = OAUTH_GET_CRED_URL + ":" + OAUTH_GET_CRED_PORT + OAUTH_GET_CRED_URI; - httpHeader.setProperty(HEADER_HOST, substringAfterLast(OAUTH_GET_CRED_URL, "/")); - logger.trace("{} from {}", step, url); - httpResponse = HttpUtil.executeUrl(HttpMethod.GET, url, httpHeader, null, null, NETWORK_TIMEOUT_MS); - logger.trace("http response = {}", httpResponse); - OauthCredentials cred = gson.fromJson(httpResponse, OauthCredentials.class); - epghttpsurl = getString(cred.epghttpsurl); - if (epghttpsurl.isEmpty()) { - throw new MagentaTVException("Unable to determine EPG url"); + Request request = httpClient.newRequest(url).method(method).timeout(NETWORK_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + for (Enumeration<?> e = headers.keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + String val = (String) headers.get(key); + request.header(key, val); } - if (!epghttpsurl.contains("/EPG")) { - epghttpsurl = epghttpsurl + "/EPG"; + if (method.equals(HttpMethod.POST)) { + fillPostData(request, data); } - logger.debug("epghttpsurl = {}", epghttpsurl); + if (cookies.size() > 0) { + // Add cookies + String cookieValue = ""; + for (HttpCookie c : cookies) { + cookieValue = cookieValue + substringBefore(c.getValue(), ";") + "; "; + } + request.header("Cookie", substringBeforeLast(cookieValue, ";")); + } + logger.debug("OAuth: HTTP Request\n\tHTTP {} {}\n\tData={}", method, url, data.isEmpty() ? "<none>" : data); + logger.trace("\n\tHeaders={}\tCookies={}", request.getHeaders(), request.getCookies()); - // get OAuth data from response - if (cred.sam3Para != null) { - for (OauthKeyValue si : cred.sam3Para) { - logger.trace("sam3Para.{} = {}", si.key, si.value); - if (si.key.equalsIgnoreCase("oAuthScope")) { - oAuthScope = si.value; - } else if (si.key.equalsIgnoreCase("SAM3ServiceURL")) { - oAuthService = si.value; - } + ContentResponse contentResponse = request.send(); + result = contentResponse.getContentAsString().replace("\t", "").replace("\r\n", "").trim(); + int status = contentResponse.getStatus(); + logger.debug("OAuth: HTTP Response\n\tStatus={} {}\n\tData={}", status, contentResponse.getReason(), + result.isEmpty() ? "<none>" : result); + logger.trace("\n\tHeaders={}", contentResponse.getHeaders()); + + // validate response, API errors are reported as Json + HttpFields responseHeaders = contentResponse.getHeaders(); + for (HttpField f : responseHeaders) { + if (f.getName().equals("Set-Cookie")) { + HttpCookie c = new HttpCookie(f.getName(), f.getValue()); + cookies.add(c); } } - if (oAuthScope.isEmpty() || oAuthService.isEmpty()) { - throw new MagentaTVException("OAuth failed: Can't get Scope and Service: " + httpResponse); + if (status != HttpStatus.OK_200) { + String error = "HTTP reqaest failed for URL " + url + ", Code=" + contentResponse.getReason() + "(" + + status + ")"; + throw new MagentaTVException(error); } - - // Get OAuth token - step = "get token"; - url = oAuthService + "/oauth2/tokens"; - logger.debug("{} from {}", step, url); - - String userId = ""; - String uuid = UUID.randomUUID().toString(); - String cnonce = MagentaTVControl.computeMD5(uuid); - // New flow based on WebTV - postData = MessageFormat.format( - "password={0}&scope={1}+offline_access&grant_type=password&username={2}&x_telekom.access_token.format=CompactToken&x_telekom.access_token.encoding=text%2Fbase64&client_id=10LIVESAM30000004901NGTVWEB0000000000000", - URLEncoder.encode(accountPassword, UTF_8), oAuthScope, URLEncoder.encode(accountName, UTF_8)); - url = oAuthService + "/oauth2/tokens"; - dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8"))); - httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS); - logger.trace("http response={}", httpResponse); - OAuthTokenResponse resp = gson.fromJson(httpResponse, OAuthTokenResponse.class); - if (resp.accessToken.isEmpty()) { - String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} ({2})", - accountName, getString(resp.errorDescription), getString(resp.error)); - logger.warn("{}", errorMessage); - throw new MagentaTVException(errorMessage); - } - - uuid = "t_" + MagentaTVControl.computeMD5(accountName); - url = "https://web.magentatv.de/EPG/JSON/DTAuthenticate?SID=user&T=Mac_chrome_81"; - postData = "{\"userType\":1,\"terminalid\":\"" + uuid + "\",\"mac\":\"" + uuid + "\"" - + ",\"terminaltype\":\"MACWEBTV\",\"utcEnable\":1,\"timezone\":\"Europe/Berlin\"," - + "\"terminalDetail\":[{\"key\":\"GUID\",\"value\":\"" + uuid + "\"}," - + "{\"key\":\"HardwareSupplier\",\"value\":\"\"},{\"key\":\"DeviceClass\",\"value\":\"PC\"}," - + "{\"key\":\"DeviceStorage\",\"value\":\"1\"},{\"key\":\"DeviceStorageSize\",\"value\":\"\"}]," - + "\"softwareVersion\":\"\",\"osversion\":\"\",\"terminalvendor\":\"Unknown\"," - + "\"caDeviceInfo\":[{\"caDeviceType\":6,\"caDeviceId\":\"" + uuid + "\"}]," + "\"accessToken\":\"" - + resp.accessToken + "\",\"preSharedKeyID\":\"PC01P00002\",\"cnonce\":\"" + cnonce + "\"}"; - dataStream = new ByteArrayInputStream(postData.getBytes(Charset.forName("UTF-8"))); - logger.debug("HTTP POST {}, postData={}", url, postData); - httpResponse = HttpUtil.executeUrl(HttpMethod.POST, url, httpHeader, dataStream, null, NETWORK_TIMEOUT_MS); - - logger.trace("http response={}", httpResponse); - OAuthAuthenticateResponse authResp = gson.fromJson(httpResponse, OAuthAuthenticateResponse.class); - if (authResp.userID.isEmpty()) { - String errorMessage = MessageFormat.format("Unable to authenticate: accountName={0}, rc={1} {2}", - accountName, getString(authResp.retcode), getString(authResp.desc)); - logger.warn("{}", errorMessage); - throw new MagentaTVException(errorMessage); - } - userId = getString(authResp.userID); - if (userId.isEmpty()) { - throw new MagentaTVException("No userID received!"); - } - String hashedUserID = MagentaTVControl.computeMD5(userId).toUpperCase(); - logger.trace("done, userID = {}", hashedUserID); - return hashedUserID; - } catch (IOException e) { - throw new MagentaTVException(e, - "Unable to authenticate {0}: {1} failed; serviceURL={2}, rc={3}/{4}, response={5}", accountName, - step, oAuthService, retcode, retmsg, httpResponse); + } catch (ExecutionException | InterruptedException | TimeoutException e) { + String error = "HTTP reqaest failed for URL " + url; + logger.info("{}", error, e); + throw new MagentaTVException(e, error); } + return result; } private Properties initHttpHeader() { Properties httpHeader = new Properties(); - httpHeader.setProperty(HEADER_USER_AGENT, OAUTH_USER_AGENT); - httpHeader.setProperty(HEADER_ACCEPT, "*/*"); - httpHeader.setProperty(HEADER_LANGUAGE, "de-de"); - httpHeader.setProperty(HEADER_CACHE_CONTROL, "no-cache"); + httpHeader.setProperty(HttpHeader.ACCEPT.toString(), "*/*"); + httpHeader.setProperty(HttpHeader.ACCEPT_LANGUAGE.toString(), "en-US,en;q=0.9,de;q=0.8"); + httpHeader.setProperty(HttpHeader.CACHE_CONTROL.toString(), "no-cache"); return httpHeader; } + private void fillPostData(Request request, String data) { + if (!data.isEmpty()) { + StringContentProvider postData; + if (request.getHeaders().contains(HttpHeader.CONTENT_TYPE)) { + String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE); + postData = new StringContentProvider(contentType, data, StandardCharsets.UTF_8); + } else { + boolean json = data.startsWith("{"); + postData = new StringContentProvider(json ? "application/json" : "application/x-www-form-urlencoded", + data, StandardCharsets.UTF_8); + } + request.content(postData); + request.header(HttpHeader.CONTENT_LENGTH, Long.toString(postData.getLength())); + } + } + private String getString(@Nullable String value) { return value != null ? value : ""; } + + private String urlEncode(String url) { + try { + return URLEncoder.encode(url, UTF_8); + } catch (UnsupportedEncodingException e) { + logger.warn("OAuth: Unable to URL encode string {}", url, e); + return ""; + } + } } diff --git a/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVPoweroffListener.java b/bundles/org.openhab.binding.magentatv/src/main/java/org/openhab/binding/magentatv/internal/network/MagentaTVPoweroffListener.java old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/binding/binding.xml old mode 100644 new mode 100755 diff --git a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties index 6374fc9c8c2..14734cd7855 100644 --- a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties +++ b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/i18n/magentatv_de.properties @@ -9,8 +9,8 @@ thing-type.magentatv.receiver.description = Media Receiver zum Epmfang von Magen # Thing configuration thing-type.config.magentatv.receiver.ipAddress.label = IP-Adresse thing-type.config.magentatv.ipAddress.description = IP Adresse des Media Receivers -thing-type.config.magentatv.receiver.userId.label = UID -thing-type.config.magentatv.receiver.userId.description = Technische Benutzerkennung (User ID), siehe Dokumentation; wird automatisch gef�llt, wenn Login-Name und Passwort angegeben sind. +thing-type.config.magentatv.receiver.userId.label = User ID +thing-type.config.magentatv.receiver.userId.description = Technische Benutzerkennung, siehe Dokumentation thing-type.config.magentatv.receiver.accountName.label = Login-Name thing-type.config.magentatv.receiver.accountName.description = Login-Name (E-Mail) zur Anmeldung im Telekom Kundencenter thing-type.config.magentatv.receiver.accountPassword.label = Passwort diff --git a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml old mode 100644 new mode 100755 index 36b0ced0376..79d4f29e644 --- a/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.magentatv/src/main/resources/OH-INF/thing/thing-types.xml @@ -20,8 +20,8 @@ </channel-group> </channel-groups> - <representation-property>macAddress</representation-property> + <config-description uri="thing-type:magentatv:receiver"> <parameter name="ipAddress" type="text"> <label>Device IP Address</label> @@ -40,7 +40,7 @@ </parameter> <parameter name="userId" type="text"> <label>User ID</label> - <description>Technical User ID required for pairing process, auto-filled when account credentials are given</description> + <description>Technical User ID required for pairing process</description> </parameter> <parameter name="udn" type="text"> <label>Unique Device Name</label>