diff options
Diffstat (limited to 'module/plugins')
| -rw-r--r-- | module/plugins/hoster/MegaCoNz.py | 93 | 
1 files changed, 57 insertions, 36 deletions
| diff --git a/module/plugins/hoster/MegaCoNz.py b/module/plugins/hoster/MegaCoNz.py index fc6724dc7..db3f8d571 100644 --- a/module/plugins/hoster/MegaCoNz.py +++ b/module/plugins/hoster/MegaCoNz.py @@ -9,11 +9,12 @@ from os import remove  from Crypto.Cipher import AES  from Crypto.Util import Counter -from pycurl import SSL_CIPHER_LIST +# from pycurl import SSL_CIPHER_LIST  from module.common.json_layer import json_loads, json_dumps  from module.plugins.Hoster import Hoster +  ############################ General errors ###################################  # EINTERNAL            (-1): An internal error has occurred. Please submit a bug report, detailing the exact circumstances in which this error occurred  # EARGS                (-2): You have passed invalid arguments to this command @@ -46,15 +47,17 @@ from module.plugins.Hoster import Hoster  class MegaCoNz(Hoster):      __name__    = "MegaCoNz"      __type__    = "hoster" -    __version__ = "0.17" +    __version__ = "0.20" -    __pattern__ = r'https?://(?:www\.)?mega\.co\.nz/#!(?P<ID>[\w!-]+)' +    __pattern__ = r'https?://(?:www\.)?mega\.co\.nz/#(?P<TYPE>N|)!(?P<ID>[\w^_]+)!(?P<KEY>[\w,\\-]+)'      __description__ = """Mega.co.nz hoster plugin"""      __license__     = "GPLv3" -    __authors__     = [("RaNaN", "ranan@pyload.org")] +    __authors__     = [("RaNaN", "ranan@pyload.org"), +                       ("Walter Purcaro", "vuolter@gmail.com")] + -    API_URL     = "https://g.api.mega.co.nz/cs" +    API_URL     = "https://eu.api.mega.co.nz/cs"      FILE_SUFFIX = ".crypted" @@ -66,12 +69,17 @@ class MegaCoNz(Hoster):      def getCipherKey(self, key):          """ Construct the cipher key from the given data """          a = array("I", key) -        key_array = array("I", [a[0] ^ a[4], a[1] ^ a[5], a[2] ^ a[6], a[3] ^ a[7]]) -        return key_array +        k        = array("I", [a[0] ^ a[4], a[1] ^ a[5], a[2] ^ a[6], a[3] ^ a[7]]) +        iv       = a[4:6] + (0, 0) +        meta_mac = a[6:8] + +        return k, iv, meta_mac -    def callApi(self, **kwargs): + +    def api_response(self, **kwargs):          """ Dispatch a call to the api, see https://mega.co.nz/#developers """ +          # generate a session id, no idea where to obtain elsewhere          uid = random.randint(10 << 9, 10 ** 10) @@ -81,8 +89,10 @@ class MegaCoNz(Hoster):      def decryptAttr(self, data, key): -        cbc = AES.new(self.getCipherKey(key), AES.MODE_CBC, "\0" * 16) -        attr = cbc.decrypt(self.b64_decode(data)) +        k, iv, meta_mac = getCipherKey(key) +        cbc             = AES.new(k, AES.MODE_CBC, "\0" * 16) +        attr            = cbc.decrypt(self.b64_decode(data)) +          self.logDebug("Decrypted Attr: " + attr)          if not attr.startswith("MEGA"):              self.fail(_("Decryption failed")) @@ -98,34 +108,37 @@ class MegaCoNz(Hoster):          n = key[16:24]          # convert counter to long and shift bytes -        ctr = Counter.new(128, initial_value=long(n.encode("hex"), 16) << 64) -        cipher = AES.new(self.getCipherKey(key), AES.MODE_CTR, counter=ctr) +        k, iv, meta_mac = getCipherKey(key) +        ctr             = Counter.new(128, initial_value=long(n.encode("hex"), 16) << 64) +        cipher          = AES.new(k, AES.MODE_CTR, counter=ctr)          self.pyfile.setStatus("decrypting") -        file_crypted = self.lastDownload +        file_crypted   = self.lastDownload          file_decrypted = file_crypted.rsplit(self.FILE_SUFFIX)[0]          try: -            f = open(file_crypted, "rb") +            f  = open(file_crypted, "rb")              df = open(file_decrypted, "wb") +          except IOError, e:              self.fail(str(e)) -        # TODO: calculate CBC-MAC for checksum +        chunk_size = 2 ** 15  # buffer size, 32k +        # file_mac   = [0, 0, 0, 0]  # calculate CBC-MAC for checksum -        size = 2 ** 15  # buffer size, 32k          while True: -            buf = f.read(size) +            buf = f.read(chunk_size)              if not buf:                  break -            df.write(cipher.decrypt(buf)) +            chunk = cipher.decrypt(buf) +            df.write(chunk)          f.close()          df.close() -        remove(file_crypted) +        remove(file_crypted)          self.lastDownload = file_decrypted @@ -133,38 +146,46 @@ class MegaCoNz(Hoster):          key = None          # match is guaranteed because plugin was chosen to handle url -        node = re.match(self.__pattern__, pyfile.url).group('ID') -        if "!" in node: -            node, key = node.split("!", 1) +        pattern = re.match(self.__pattern__, pyfile.url).groupdict() +        node    = pattern['ID'] +        key     = pattern['KEY'] +        public  = pattern['TYPE'] != 'N' -        self.logDebug("ID: %s | Key: %s" % (node, key)) +        self.logDebug("ID: %s" % node, "Key: %s" % key, "Type: %s" % ("public" if public else "node")) -        if not key: -            self.fail(_("No file key provided in the URL")) +        key = self.b64_decode(key)          # g is for requesting a download url          # this is similar to the calls in the mega js app, documentation is very bad -        dl = self.callApi(a="g", g=1, p=node, ssl=1)[0] +        if public: +            dl = self.api_response(a="g", g=1, p=node, ssl=1)[0] +        else: +            dl = self.api_response(a="g", g=1, n=node, ssl=1)[0]          if "e" in dl: -            e = dl['e'] -            # ETEMPUNAVAIL (-18): Resource temporarily not available, please try again later -            if e == -18: -                self.retry() -            else: -                self.fail(_("Error code:") + e) +            ecode = -dl['e'] -        # TODO: map other error codes, e.g -        # EACCESS (-11): Access violation (e.g., trying to write to a read-only share) +            if ecode in (9, 16, 21): +                self.offline() + +            elif ecode in (3, 13, 17, 18, 19): +                self.tempOffline() + +            elif ecode in (1, 4, 6, 10, 15, 21): +                self.retry(5, 30, _("Error code: [%s]") % -ecode) + +            else: +                self.fail(_("Error code: [%s]") % -ecode) -        key = self.b64_decode(key)          attr = self.decryptAttr(dl['at'], key)          pyfile.name = attr['n'] + self.FILE_SUFFIX +        pyfile.size = dl['s'] -        self.req.http.c.setopt(SSL_CIPHER_LIST, "RC4-MD5:DEFAULT") +        # self.req.http.c.setopt(SSL_CIPHER_LIST, "RC4-MD5:DEFAULT")          self.download(dl['g']) +          self.decryptFile(key)          # Everything is finished and final name can be set | 
