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

import base64
import binascii
import re

from Crypto.Cipher import AES
from module.plugins.Crypter import Crypter
from module.plugins.internal.CaptchaService import ReCaptcha


class NCryptIn(Crypter):
    __name__ = "NCryptIn"
    __type__ = "crypter"
    __pattern__ = r'http://(?:www\.)?ncrypt.in/(?P<type>folder|link|frame)-([^/\?]+)'
    __version__ = "1.32"
    __description__ = """NCrypt.in decrypter plugin"""
    __author_name__ = ("fragonib", "stickell")
    __author_mail__ = ("fragonib[AT]yahoo[DOT]es", "l.stickell@yahoo.it")

    # Constants
    _JK_KEY_ = "jk"
    _CRYPTED_KEY_ = "crypted"

    NAME_PATTERN = r'<meta name="description" content="(?P<N>[^"]+)"'

    def setup(self):
        self.package = None
        self.html = None
        self.cleanedHtml = None
        self.links_source_order = ['cnl2', 'rsdf', 'ccf', 'dlc', 'web']
        self.protection_type = None

    def decrypt(self, pyfile):

        # Init
        self.package = pyfile.package()
        package_links = []
        package_name = self.package.name
        folder_name = self.package.folder

        # Deal with single links
        if self.isSingleLink():
            package_links.extend(self.handleSingleLink())

        # Deal with folders
        else:

            # Request folder home
            self.html = self.requestFolderHome()
            self.cleanedHtml = self.removeHtmlCrap(self.html)
            if not self.isOnline():
                self.offline()

            # Check for folder protection    
            if self.isProtected():
                self.html = self.unlockProtection()
                self.cleanedHtml = self.removeHtmlCrap(self.html)
                self.handleErrors()

            # Prepare package name and folder
            (package_name, folder_name) = self.getPackageInfo()

            # Extract package links
            for link_source_type in self.links_source_order:
                package_links.extend(self.handleLinkSource(link_source_type))
                if package_links:  # use only first source which provides links
                    break
            package_links = set(package_links)

        # Pack and return links
        if not package_links:
            self.fail('Could not extract any links')
        self.packages = [(package_name, package_links, folder_name)]

    def isSingleLink(self):
        link_type = re.match(self.__pattern__, self.pyfile.url).group('type')
        return link_type in ('link', 'frame')

    def requestFolderHome(self):
        return self.load(self.pyfile.url, decode=True)

    def removeHtmlCrap(self, content):
        patterns = (r'(type="hidden".*?(name=".*?")?.*?value=".*?")',
                    r'display:none;">(.*?)</(div|span)>',
                    r'<div\s+class="jdownloader"(.*?)</div>',
                    r'<table class="global">(.*?)</table>',
                    r'<iframe\s+style="display:none(.*?)</iframe>')
        for pattern in patterns:
            rexpr = re.compile(pattern, re.DOTALL)
            content = re.sub(rexpr, "", content)
        return content

    def isOnline(self):
        if "Your folder does not exist" in self.cleanedHtml:
            self.logDebug("File not found")
            return False
        return True

    def isProtected(self):
        form_match = re.search(r'<form.*?name.*?protected.*?>(.*?)</form>', self.cleanedHtml, re.DOTALL)
        if form_match:
            form_content = form_match.group(1)
            for keyword in ("password", "captcha"):
                if keyword in form_content:
                    self.protection_type = keyword
                    self.logDebug("Links are %s protected" % self.protection_type)
                    return True
        return False

    def getPackageInfo(self):
        m = re.search(self.NAME_PATTERN, self.html)
        if m:
            name = folder = m.group('N').strip()
            self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder))
        else:
            name = self.package.name
            folder = self.package.folder
            self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder))
        return name, folder

    def unlockProtection(self):

        postData = {}

        form = re.search(r'<form name="protected"(.*?)</form>', self.cleanedHtml, re.DOTALL).group(1)

        # Submit package password
        if "password" in form:
            password = self.getPassword()
            self.logDebug("Submitting password [%s] for protected links" % password)
            postData['password'] = password

        # Resolve anicaptcha
        if "anicaptcha" in form:
            self.logDebug("Captcha protected")
            captchaUri = re.search(r'src="(/temp/anicaptcha/[^"]+)', form).group(1)
            captcha = self.decryptCaptcha("http://ncrypt.in" + captchaUri)
            self.logDebug("Captcha resolved [%s]" % captcha)
            postData['captcha'] = captcha

        # Resolve recaptcha           
        if "recaptcha" in form:
            self.logDebug("ReCaptcha protected")
            captcha_key = re.search(r'\?k=(.*?)"', form).group(1)
            self.logDebug("Resolving ReCaptcha with key [%s]" % captcha_key)
            recaptcha = ReCaptcha(self)
            challenge, code = recaptcha.challenge(captcha_key)
            postData['recaptcha_challenge_field'] = challenge
            postData['recaptcha_response_field'] = code

        # Resolve circlecaptcha
        if "circlecaptcha" in form:
            self.logDebug("CircleCaptcha protected")
            captcha_img_url = "http://ncrypt.in/classes/captcha/circlecaptcha.php"
            coords = self.decryptCaptcha(captcha_img_url, forceUser=True, imgtype="png", result_type='positional')
            self.logDebug("Captcha resolved, coords [%s]" % str(coords))
            postData['circle.x'] = coords[0]
            postData['circle.y'] = coords[1]

        # Unlock protection
        postData['submit_protected'] = 'Continue to folder'
        return self.load(self.pyfile.url, post=postData, decode=True)

    def handleErrors(self):

        if self.protection_type == "password":
            if "This password is invalid!" in self.cleanedHtml:
                self.logDebug("Incorrect password, please set right password on 'Edit package' form and retry")
                self.fail("Incorrect password, please set right password on 'Edit package' form and retry")

        if self.protection_type == "captcha":
            if "The securitycheck was wrong!" in self.cleanedHtml:
                self.logDebug("Invalid captcha, retrying")
                self.invalidCaptcha()
                self.retry()
            else:
                self.correctCaptcha()

    def handleLinkSource(self, link_source_type):

        # Check for JS engine
        require_js_engine = link_source_type in ('cnl2', 'rsdf', 'ccf', 'dlc')
        if require_js_engine and not self.js:
            self.logDebug("No JS engine available, skip %s links" % link_source_type)
            return []

        # Select suitable handler
        if link_source_type == 'single':
            return self.handleSingleLink()
        if link_source_type == 'cnl2':
            return self.handleCNL2()
        elif link_source_type in ('rsdf', 'ccf', 'dlc'):
            return self.handleContainer(link_source_type)
        elif link_source_type == 'web':
            return self.handleWebLinks()
        else:
            self.fail('unknown source type "%s" (this is probably a bug)' % link_source_type)

    def handleSingleLink(self):

        self.logDebug("Handling Single link")
        package_links = []

        # Decrypt single link
        decrypted_link = self.decryptLink(self.pyfile.url)
        if decrypted_link:
            package_links.append(decrypted_link)

        return package_links

    def handleCNL2(self):

        self.logDebug("Handling CNL2 links")
        package_links = []

        if 'cnl2_output' in self.cleanedHtml:
            try:
                (vcrypted, vjk) = self._getCipherParams()
                for (crypted, jk) in zip(vcrypted, vjk):
                    package_links.extend(self._getLinks(crypted, jk))
            except:
                self.fail("Unable to decrypt CNL2 links")

        return package_links

    def handleContainers(self):

        self.logDebug("Handling Container links")
        package_links = []

        pattern = r"/container/(rsdf|dlc|ccf)/([a-z0-9]+)"
        containersLinks = re.findall(pattern, self.html)
        self.logDebug("Decrypting %d Container links" % len(containersLinks))
        for containerLink in containersLinks:
            link = "http://ncrypt.in/container/%s/%s.%s" % (containerLink[0], containerLink[1], containerLink[0])
            package_links.append(link)

        return package_links

    def handleWebLinks(self):

        self.logDebug("Handling Web links")
        pattern = r"(http://ncrypt\.in/link-.*?=)"
        links = re.findall(pattern, self.html)

        package_links = []
        self.logDebug("Decrypting %d Web links" % len(links))
        for i, link in enumerate(links):
            self.logDebug("Decrypting Web link %d, %s" % (i + 1, link))
            decrypted_link = self.decrypt(link)
            if decrypted_link:
                package_links.append(decrypted_link)

        return package_links

    def decryptLink(self, link):
        try:
            url = link.replace("link-", "frame-")
            link = self.load(url, just_header=True)['location']
            return link
        except Exception, detail:
            self.logDebug("Error decrypting link %s, %s" % (link, detail))

    def _getCipherParams(self):

        pattern = r'<input.*?name="%s".*?value="(.*?)"'

        # Get jk
        jk_re = pattern % NCryptIn._JK_KEY_
        vjk = re.findall(jk_re, self.html)

        # Get crypted
        crypted_re = pattern % NCryptIn._CRYPTED_KEY_
        vcrypted = re.findall(crypted_re, self.html)

        # Log and return
        self.logDebug("Detected %d crypted blocks" % len(vcrypted))
        return vcrypted, vjk

    def _getLinks(self, crypted, jk):

        # Get key
        jreturn = self.js.eval("%s f()" % jk)
        self.logDebug("JsEngine returns value [%s]" % jreturn)
        key = binascii.unhexlify(jreturn)

        # Decode crypted
        crypted = base64.standard_b64decode(crypted)

        # Decrypt
        Key = key
        IV = key
        obj = AES.new(Key, AES.MODE_CBC, IV)
        text = obj.decrypt(crypted)

        # Extract links
        text = text.replace("\x00", "").replace("\r", "")
        links = text.split("\n")
        links = filter(lambda x: x != "", links)

        # Log and return
        self.logDebug("Block has %d links" % len(links))
        return links
