# -*- coding: utf-8 -*-

#2021-11-29

import io, gzip, time, re
import socket, os, sys, hashlib, json

try:
    import xbmcgui
    from resources.lib.control import dataPath, addonName
    ADDON_NAME = addonName
    profilePath = dataPath
except ImportError:
    ADDON_NAME = ''
    profilePath = ''

is_python2 = sys.version_info.major == 2

if is_python2:
    from urlparse import urlparse
    from urllib import quote, urlencode
    from urllib2 import HTTPError, URLError, HTTPHandler, HTTPSHandler, HTTPCookieProcessor, build_opener, Request, HTTPRedirectHandler
    from cookielib import LWPCookieJar, Cookie
    from httplib import HTTPException
else:
    from urllib.parse import quote, urlencode, urlparse
    from urllib.error import HTTPError, URLError
    from urllib.request import HTTPHandler, HTTPSHandler, HTTPCookieProcessor, build_opener, Request, HTTPRedirectHandler
    from http.cookiejar import LWPCookieJar, Cookie
    from http.client import HTTPException


class cRequestHandler:
    def __init__(self, sUrl, caching=True, ignoreErrors=True, compression=True, jspost=False):
        self._sUrl = sUrl
        self._sRealUrl = ''
        self._USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0'
        self._aParameters = {}
        self._headerEntries = {}
        self._profilePath = profilePath  # common.profilePath
        self._cachePath = ''
        self._cookiePath = ''
        self._Status = ''
        self._sResponseHeader = ''
        self.ignoreDiscard(True)
        self.ignoreExpired(False)
        self.caching = caching
        self.ignoreErrors = ignoreErrors
        self.compression = compression
        self.jspost = jspost
        self.cacheTime = 600  # int(cConfig().getSetting('cacheTime', 600))
        self.requestTimeout = 7  # int(cConfig().getSetting('requestTimeout', 10))
        self.removeBreakLines(True)
        self.removeNewLines(True)
        self.__setDefaultHeader()
        self.__setCachePath()
        self.__setCookiePath()
        socket.setdefaulttimeout(self.requestTimeout)
        self.printErrors = False 

    def getStatus(self):
        return self._Status

    def removeNewLines(self, bRemoveNewLines):
        self.__bRemoveNewLines = bRemoveNewLines

    def removeBreakLines(self, bRemoveBreakLines):
        self.__bRemoveBreakLines = bRemoveBreakLines

    def addHeaderEntry(self, sHeaderKey, sHeaderValue):
        self._headerEntries[sHeaderKey] = sHeaderValue

    def getHeaderEntry(self, sHeaderKey):
        if sHeaderKey in self._headerEntries:
            return self._headerEntries[sHeaderKey]

    def addParameters(self, key, value, Quote=False):
        if not Quote:
            self._aParameters[key] = value
        else:
            self._aParameters[key] = quote(str(value))

    def getResponseHeader(self):
        return self._sResponseHeader

    def getRealUrl(self):
        return self._sRealUrl

    def getRequestUri(self):
        return self._sUrl + '?' + urlencode(self._aParameters)

    def __setDefaultHeader(self):
        self.addHeaderEntry('User-Agent', self._USER_AGENT)
        self.addHeaderEntry('Accept-Language', 'de,en-US;q=0.7,en;q=0.3')
        self.addHeaderEntry('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
        if self.compression:
            self.addHeaderEntry('Accept-Encoding', 'gzip, deflate')

    def request(self):
        self._sUrl = self._sUrl.replace(' ', '+')
        if self.caching and self.cacheTime > 0:
            sContent = self.readCache(self.getRequestUri())
            if sContent:
                return sContent
        cookieJar = LWPCookieJar(filename=self._cookiePath)
        try:
            cookieJar.load(ignore_discard=self.__bIgnoreDiscard, ignore_expires=self.__bIgnoreExpired)
        except Exception as e:
            if self.printErrors: print(e) 
        if self.jspost:
            if is_python2:
                sParameters = json.dumps(self._aParameters)
            else:
                sParameters = json.dumps(self._aParameters).encode()
        else:
            if is_python2:
                sParameters = urlencode(self._aParameters, True)
            else:
                sParameters = urlencode(self._aParameters, True).encode()


        # urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] - kinoger fsst
        # https://coderedirect.com/questions/515348/python-3-urllib-ignore-ssl-certificate-verification
        # ssl._create_default_https_context = ssl._create_unverified_context
        import ssl
        if is_python2:
            ssl_context = ssl.create_default_context()
        else:
            ssl_context = ssl.create_default_context()
            ssl_context.check_hostname = False
            ssl_context.verify_mode = ssl.CERT_NONE

        handlers = [HTTPHandler(), HTTPSHandler(context=ssl_context), HTTPCookieProcessor(cookiejar=cookieJar), RedirectFilter()]
        opener = build_opener(*handlers)
        oRequest = Request(self._sUrl, sParameters if len(sParameters) > 0 else None)

        for key, value in self._headerEntries.items():
            oRequest.add_header(key, value)
        if self.jspost:
            oRequest.add_header('Content-Type', 'application/json')
        cookieJar.add_cookie_header(oRequest)
        try:
            oResponse = opener.open(oRequest)
        except HTTPError as e:
            if e.code == 503:
                oResponse = None
                if not oResponse:
                    if self.printErrors: print('Failed Cloudflare aktiv Url: ' + self._sUrl)
            if e.code == 403:
                data = e.fp.read()
                if 'DDOS-GUARD' in str(data):
                    opener = build_opener(HTTPCookieProcessor(cookieJar))
                    opener.addheaders = [('User-agent', self._USER_AGENT), ('Referer', self._sUrl)]
                    response = opener.open('https://check.ddos-guard.net/check.js')
                    if is_python2:
                        content = response.read()
                    else:
                        content = response.read().decode('utf-8').encode('utf-8', 'replace').decode('utf-8')
                    url2 = re.findall("Image.*?'([^']+)'; new", content)
                    url3 = urlparse(self._sUrl)
                    url3 = '%s://%s/%s' % (url3.scheme, url3.netloc, url2[0])
                    opener = build_opener(HTTPCookieProcessor(cookieJar))
                    opener.addheaders = [('User-agent', self._USER_AGENT), ('Referer', self._sUrl)]
                    opener.open(url3).read()
                    opener = build_opener(HTTPCookieProcessor(cookieJar))
                    opener.addheaders = [('User-agent', self._USER_AGENT), ('Referer', self._sUrl)]
                    oResponse = opener.open(self._sUrl, sParameters if len(sParameters) > 0 else None)
                    if not oResponse:
                        if not self.ignoreErrors:
                            print('Failed DDOS-GUARD Url: ' + self._sUrl)
                        return ''
                else:
                    return ''
            elif not self.ignoreErrors:
                xbmcgui.Dialog().ok(ADDON_NAME, 'Fehler beim Abrufen der Url: {0} {1}'.format(self._sUrl, str(e)))
                if self.printErrors: print('HTTPError ' + str(e) + ' Url: ' + self._sUrl)
                return ''
            else:
                oResponse = e
        except URLError as e:
            if not self.ignoreErrors:
                xbmcgui.Dialog().ok(ADDON_NAME, str(e.reason))
            if self.printErrors: print('URLError ' + str(e.reason) + ' Url: ' + self._sUrl)
            return ''
        except HTTPException as e:
            if not self.ignoreErrors:
                xbmcgui.Dialog().ok(ADDON_NAME, str(e))
            if self.printErrors: print('HTTPException ' + str(e) + ' Url: ' + self._sUrl)
            return ''

        self._sResponseHeader = oResponse.info()
        if self._sResponseHeader.get('Content-Encoding') == 'gzip':
            sContent = gzip.GzipFile(fileobj=io.BytesIO(oResponse.read())).read()
            if not is_python2:
                sContent = sContent.decode('utf-8').encode('utf-8', 'replace').decode('utf-8')
        else:
            if is_python2:
                sContent = oResponse.read()
            else:
                sContent = oResponse.read().decode('utf-8').encode('utf-8', 'replace').decode('utf-8')
        if 'lazingfast' in sContent:
            bf = cBF().resolve(self._sUrl, sContent, cookieJar, self._USER_AGENT, sParameters)
            if bf:
                sContent = bf
            else:
                if self.printErrors: print('Failed BF Url: ' + self._sUrl)
        try:
            cookieJar.save(ignore_discard=self.__bIgnoreDiscard, ignore_expires=self.__bIgnoreExpired)
        except Exception as e:
            if self.printErrors: print('Failed save cookie: %s' % e)
        if self.__bRemoveNewLines:
            sContent = sContent.replace('\n', '').replace('\r\t', '')
        if self.__bRemoveBreakLines:
            sContent = sContent.replace('&nbsp;', '')
        self._sRealUrl = oResponse.geturl()
        self._Status = oResponse.getcode() if self._sUrl == self._sRealUrl else '301'
        oResponse.close()
        if self.caching and self.cacheTime > 0:
            self.writeCache(self.getRequestUri(), sContent)
        return sContent

    def __setCookiePath(self):
        cookieFile = os.path.join(self._profilePath, 'cookies')
        if not os.path.exists(cookieFile):
            os.makedirs(cookieFile)
        if 'dummy' not in self._sUrl:
            cookieFile = os.path.join(cookieFile, urlparse(self._sUrl).netloc.replace('.', '_') + '.txt')
            if not os.path.exists(cookieFile):
                open(cookieFile, 'w').close()
            self._cookiePath = cookieFile

    def getCookie(self, sCookieName, sDomain=''):
        cookieJar = LWPCookieJar()
        try:
            cookieJar.load(self._cookiePath, self.__bIgnoreDiscard, self.__bIgnoreExpired)
        except Exception as e:
            if self.printErrors: print(e)
        for entry in cookieJar:
            if entry.name == sCookieName:
                if sDomain == '':
                    return entry
                elif entry.domain == sDomain:
                    return entry
        return False

    def setCookie(self, oCookie):
        cookieJar = LWPCookieJar()
        try:
            cookieJar.load(self._cookiePath, self.__bIgnoreDiscard, self.__bIgnoreExpired)
            cookieJar.set_cookie(oCookie)
            cookieJar.save(self._cookiePath, self.__bIgnoreDiscard, self.__bIgnoreExpired)
        except Exception as e:
            if self.printErrors: print(e)

    def ignoreDiscard(self, bIgnoreDiscard):
        self.__bIgnoreDiscard = bIgnoreDiscard

    def ignoreExpired(self, bIgnoreExpired):
        self.__bIgnoreExpired = bIgnoreExpired

    def __setCachePath(self):
        cache = os.path.join(self._profilePath, 'htmlcache')
        if not os.path.exists(cache):
            os.makedirs(cache)
        self._cachePath = cache

    def readCache(self, url):
        content = ''
        if is_python2:
            h = hashlib.md5(url).hexdigest()
        else:
            h = hashlib.md5(url.encode('utf8')).hexdigest()
        cacheFile = os.path.join(self._cachePath, h)
        fileAge = self.getFileAge(cacheFile)
        if 0 < fileAge < self.cacheTime:
            try:
                if is_python2:
                    with open(cacheFile, 'r') as f:
                        content = f.read()
                else:
                    with open(cacheFile, 'rb') as f:
                        content = f.read().decode('utf8')
            except Exception:
                if self.printErrors: print('Could not read Cache')
            if content:
                if self.printErrors: print('read html for %s from cache' % url)
                return content
        return ''

    def writeCache(self, url, content):
        try:
            if is_python2:
                h = hashlib.md5(url).hexdigest()
                with open(os.path.join(self._cachePath, h), 'w') as f:
                    f.write(content)
            else:
                h = hashlib.md5(url.encode('utf8')).hexdigest()
                with open(os.path.join(self._cachePath, h), 'wb') as f:
                    f.write(content.encode('utf8'))
        except Exception:
            if self.printErrors: print('Could not write Cache')

    @staticmethod
    def getFileAge(cacheFile):
        try:
            return time.time() - os.stat(cacheFile).st_mtime
        except Exception:
            return 0

    def clearCache(self):
        files = os.listdir(self._cachePath)
        for file in files:
            os.remove(os.path.join(self._cachePath, file))


class cBF:
    def resolve(self, url, html, cookie_jar, user_agent, sParameters):
        page = urlparse(url).scheme + '://' + urlparse(url).netloc
        j = re.findall('<script[^>]src="([^"]+)', html)
        if j:
            opener = build_opener(HTTPCookieProcessor(cookie_jar))
            opener.addheaders = [('User-agent', user_agent), ('Referer', url)]
            opener.open(page + j[0])
        a = re.findall('xhr\.open\("GET","([^,]+)",', html)
        if a:
            import random
            aespage = page + a[0].replace('" + ww +"', str(random.randint(700, 1500)))
            opener = build_opener(HTTPCookieProcessor(cookie_jar))
            opener.addheaders = [('User-agent', user_agent), ('Referer', url)]
            if is_python2:
                html = opener.open(aespage).read()
            else:
                html = opener.open(aespage).read().decode('utf-8').encode('utf-8', 'replace').decode('utf-8')
            cval = self.aes_decode(html)
            cdata = re.findall('cookie="([^="]+).*?domain[^>]=([^;]+)', html)
            if cval and cdata:
                c = Cookie(version=0, name=cdata[0][0], value=cval, port=None, port_specified=False, domain=cdata[0][1], domain_specified=True, domain_initial_dot=False, path="/", path_specified=True, secure=False, expires=time.time() + 21600, discard=False, comment=None, comment_url=None, rest={})
                cookie_jar.set_cookie(c)
                opener = build_opener(HTTPCookieProcessor(cookie_jar))
                opener.addheaders = [('User-agent', user_agent), ('Referer', url)]
                if is_python2:
                    return opener.open(url, sParameters if len(sParameters) > 0 else None).read()
                else:
                    return opener.open(url, sParameters if len(sParameters) > 0 else None).read().decode('utf-8').encode('utf-8', 'replace').decode('utf-8')

    def aes_decode(self, html):
        try:
            from resources.lib import pyaes
            keys = re.findall('toNumbers\("([^"]+)"', html)
            if keys:
                from binascii import hexlify, unhexlify
                msg = unhexlify(keys[2])
                key = unhexlify(keys[0])
                iv = unhexlify(keys[1])
                decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
                plain_text = decrypter.feed(msg)
                plain_text += decrypter.feed()
                return hexlify(plain_text).decode()
        except Exception as e:
            print(e)


class RedirectFilter(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, hdrs, newurl):
        if 'notice.cuii' in newurl: #'https://notice.cuii.info/'
            return None
        return HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, hdrs, newurl)
