def remove_device(self, uid):
        """ Remove the specified device.

        :param str uid:                 The ID of the device to remove.
        """
        util.http_post(SOLOCOO_API + '/devices',
                       token_bearer=self._account.jwt_token,
                       data={'delete': [uid]})
    def verify_pin(self, pin):
        """ Verify the PIN.

        :param str pin:                 The PIN to validate.

        :returns:                       Returns true if the PIN is valid.
        :rtype: boolean
        """
        try:
            util.http_post(SOLOCOO_API + '/pin/parental/verify',
                           token_bearer=self._tokens.jwt_token,
                           data={
                               "pin": pin,
                           })
            return True
        except HTTPError:
            return False
 def _get_jwt_token(self, sapi_token, device_name, device_serial):
     reply = util.http_post(SOLOCOO_API + '/session',
                            data=dict(
                                sapiToken=sapi_token,
                                deviceModel=device_name,
                                deviceType="PC",
                                deviceSerial=device_serial,
                                osVersion="Linux undefined",
                                appVersion="84.0",
                                memberId="0",
                                brand=self._tenant.get('app'),
                            ))
     return json.loads(reply.text).get('token')
    def get_stream(self, asset_id):
        """ Get stream information for the requested asset.

        :param str asset_id:            The ID of the asset

        :returns:                       Information on how to play this asset.
        :rtype: StreamInfo
        """
        _LOGGER.debug('Requesting stream info for channel %s', asset_id)
        try:
            reply = util.http_post(
                SOLOCOO_API +
                '/assets/{asset_id}/play'.format(asset_id=asset_id),
                token_bearer=self._tokens.jwt_token,
                data={
                    "player": {
                        "name": "Bitmovin",
                        "version": "8.22.0",
                        "capabilities": {
                            "mediaTypes":
                            ["DASH"],  # ["DASH", "HLS", "MSSS", "Unspecified"]
                            "drmSystems": ["Widevine"]
                        },
                        "drmSystems": ["Widevine"]
                    }
                })
        except HTTPError as ex:
            if ex.response.status_code == 402:
                raise NotAvailableInOfferException
            if ex.response.status_code == 403:
                raise UnavailableException
            if ex.response.status_code == 404:
                raise UnavailableException
            raise

        data = json.loads(reply.text)

        stream = StreamInfo(
            url=data.get('url'),
            protocol=data.get('mediaType'),
            drm_protocol=data.get('drm', {}).get('system'),
            drm_license_url=data.get('drm', {}).get('licenseUrl'),
            drm_certificate=data.get('drm', {}).get('cert'),
        )

        return stream
    def _get_oauth_code(self, username, password):
        """ Do login with the sso and return the OAuth code.

        :param str username:            The username to authenticate with.
        :param str password:            The password to authenticate with.

        :returns:                       An OAuth code we can use to continue authenticating.
        :rtype: string
        """
        # This is probably not necessary for all providers, and this also might need some factory pattern to support
        # other providers.

        # Ask to forward us to the login form.
        login_page = util.http_get(
            'https://{domain}/{env}/sso.aspx'.format(
                domain=self._tenant.get('domain'),
                env=self._tenant.get('env')),
            params=dict(
                a=self._tenant.get('app'),
                s=time.time() * 100,  # unixtime in milliseconds
                d='PC',
            ))

        # Extract the path from the form, the form posts to /
        form_action = re.compile(r'<form action="([^"]+)"').search(
            login_page.text).group(1)
        login_url = urljoin(login_page.url, form_action)

        # Submit credentials
        reply = util.http_post(login_url,
                               form=dict(
                                   Username=username,
                                   Password=password,
                               ))

        # We should get redirected, if not, there was an issue with the credentials.
        if not reply.history:
            raise InvalidLoginException

        # Extract query parameters from redirected url
        params = parse_qs(urlparse(reply.url).query)

        return params.get('code')[0]
    def _do_challenge(self, device_name, device_serial, username, password):
        """ Do an authentication challenge.

        :param str device_name:         The device name of this running Kodi instance.
        :param str device_serial:       The device serial of this running Kodi instance.
        :param str username:            The username to authenticate with.
        :param str password:            The password to authenticate with.

        :return: A tuple (challenge_id, challenge_secret) that can be used to fetch a token.
        :rtype: tuple(str, str)
        """
        if username and password:
            # Do authenticated login
            oauth_code = self._get_oauth_code(username, password)
            data = dict(
                autotype='nl',
                app=self._tenant.get('app'),
                prettyname=device_name,
                model='web',
                serial=device_serial,
                oauthcode=oauth_code,
                apikey='',
            )
        else:
            # Do anonymous login
            data = dict(
                autotype='nl',
                app=self._tenant.get('app'),
                prettyname=device_name,
                model='web',
                serial=device_serial,
            )

        # TODO: catch exception
        reply = util.http_post('https://{domain}/{env}/challenge.aspx'.format(
            domain=self._tenant.get('domain'), env=self._tenant.get('env')),
                               data=data)
        challenge = json.loads(reply.text)

        return challenge.get('id'), challenge.get('secret')
    def _get_jwt_token(self, sapi_token, device_name, device_serial):
        """ Get a JWT token.

        :param str sapi_token:          The SAPI token we got when authenticating.
        :param str device_name:         The name of this device.
        :param str device_serial:       The serial number of this device.

        :returns:                       A JWT token.
        :rtype: str
        """
        reply = util.http_post(SOLOCOO_API + '/session',
                               data=dict(
                                   sapiToken=sapi_token,
                                   deviceModel=device_name,
                                   deviceType="PC",
                                   deviceSerial=device_serial,
                                   osVersion="Linux undefined",
                                   appVersion="84.0",
                                   memberId="0",
                                   brand=self._tenant.get('app'),
                               ))
        return json.loads(reply.text).get('token')
    def _get_aspx_cookie(self, challenge_id, challenge_secret, device_serial):
        """ Get an ASPX cookie.

        :param str challenge_id:        The challenge ID we got from logging in.
        :param str challenge_secret:    The challenge secret we got from logging in.
        :param str device_serial:       The device serial of this running Kodi instance.

        :returns:                       An ASPX token.
        :rtype: str
        """
        reply = util.http_post('https://{domain}/{env}/login.aspx'.format(
            domain=self._tenant.get('domain'), env=self._tenant.get('env')),
                               form=dict(
                                   secret=challenge_id + '\t' +
                                   challenge_secret,
                                   uid=device_serial,
                                   app=self._tenant.get('app'),
                               ))

        # We got redirected, and the last response doesn't contain the cookie we need.
        # We need to get it from the redirect history.
        return reply.history[0].cookies.get('.ASPXAUTH')
    def _get_oauth_code(self, username, password):
        """ Do login with the sso and return the OAuth code.

        :return: An OAuth code we can use to continue authenticating.
        :rtype: string
        """
        # This is probably not necessary for all providers, and this also might need some factory pattern to support
        # other providers.

        # Ask to forward us to the login form.
        util.http_get(
            'https://{domain}/{env}/sso.aspx'.format(
                domain=self._tenant.get('domain'),
                env=self._tenant.get('env')),
            params=dict(
                a=self._tenant.get('app'),
                s=time.time() * 100,  # unixtime in milliseconds
            ))

        # TODO: we could extract the correct url from the form in the html, but this is probably provider dependant anyway.

        # Submit credentials
        reply = util.http_post(
            'https://{auth}/'.format(auth=self._tenant.get('auth')),
            form=dict(
                Username=username,
                Password=password,
            ))

        if 'De gebruikersnaam of het wachtwoord dat u heeft ingegeven is niet correct' in reply.text:
            raise InvalidLoginException

        # Extract query parameters from redirected url
        params = parse_qs(urlparse(reply.url).query)

        return params.get('code')[0]