summaryrefslogtreecommitdiffstats
path: root/module/plugins/hoster
diff options
context:
space:
mode:
authorGravatar Walter Purcaro <vuolter@gmail.com> 2014-12-28 11:52:57 +0100
committerGravatar Walter Purcaro <vuolter@gmail.com> 2014-12-28 11:52:57 +0100
commit4ef28917ae8be8a0c6f5374bac6116fd1c469bd3 (patch)
tree789f7097a0d3accee857551c12954a4631d5543c /module/plugins/hoster
parent[ZippyshareCom] Typo (diff)
downloadpyload-4ef28917ae8be8a0c6f5374bac6116fd1c469bd3.tar.xz
[MegaCoNz] Private file support
Diffstat (limited to 'module/plugins/hoster')
-rw-r--r--module/plugins/hoster/MegaCoNz.py93
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