From 7e4d2ee557fb89c955fef79f86989e6aefb784d5 Mon Sep 17 00:00:00 2001 From: gissehel Date: Fri, 19 Apr 2013 19:53:57 +0200 Subject: [PATCH 1/3] You can't download files in shared folders using public links because public links can't be obtained from shared files. You need to download them directly. --- mega/mega.py | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/mega/mega.py b/mega/mega.py index f1be9eb..4d68a52 100644 --- a/mega/mega.py +++ b/mega/mega.py @@ -135,6 +135,8 @@ class Mega(object): # folder else: k = key + file['key'] = key + file['k'] = k attributes = base64_url_decode(file['a']) attributes = decrypt_attr(attributes, k) file['a'] = attributes @@ -222,9 +224,9 @@ class Mega(object): file = file[1] if 'h' in file and 'k' in file: public_handle = self.api_request({'a': 'l', 'n': file['h']}) - file_key = file['k'][file['k'].index(':') + 1:] - decrypted_key = a32_to_base64(decrypt_key(base64_to_a32(file_key), - self.master_key)) + if public_handle == -11 : + raise RequestError("Can't get a public link from that file (is this a shared file?)") + decrypted_key = a32_to_base64(file['key']) return '{0}://{1}/#!{2}!{3}'.format(self.schema, self.domain, public_handle, @@ -360,8 +362,7 @@ class Mega(object): """ Download a file by it's file object """ - url = self.get_link(file) - self.download_url(url, dest_path) + self.download_file(None, None, file=file[1], dest_path=dest_path, is_public=False) def download_url(self, url, dest_path=None): """ @@ -372,18 +373,29 @@ class Mega(object): file_key = path[1] self.download_file(file_id, file_key, dest_path, is_public=True) - def download_file(self, file_handle, file_key, dest_path=None, is_public=False): - if is_public: - file_key = base64_to_a32(file_key) - file_data = self.api_request({'a': 'g', 'g': 1, 'p': file_handle}) - else: - file_data = self.api_request({'a': 'g', 'g': 1, 'n': file_handle}) + def download_file(self, file_handle, file_key, dest_path=None, is_public=False, file=None): + if file is None : + if is_public: + file_key = base64_to_a32(file_key) + file_data = self.api_request({'a': 'g', 'g': 1, 'p': file_handle}) + else : + file_data = self.api_request({'a': 'g', 'g': 1, 'n': file_handle}) - k = (file_key[0] ^ file_key[4], file_key[1] ^ file_key[5], - file_key[2] ^ file_key[6], file_key[3] ^ file_key[7]) - iv = file_key[4:6] + (0, 0) - meta_mac = file_key[6:8] + k = (file_key[0] ^ file_key[4], file_key[1] ^ file_key[5], + file_key[2] ^ file_key[6], file_key[3] ^ file_key[7]) + iv = file_key[4:6] + (0, 0) + meta_mac = file_key[6:8] + else : + file_data = self.api_request({'a': 'g', 'g': 1, 'n': file['h']}) + k = file['k'] + iv = file['iv'] + meta_mac = file['meta_mac'] + # Seems to happens sometime... When this occurs, files are + # inaccessible also in the official also in the official webapp. + # Strangely, files can come back later. + if 'g' not in file_data : + raise RequestError('File not accessible anymore') file_url = file_data['g'] file_size = file_data['s'] attribs = base64_url_decode(file_data['at']) From 09c4b14c976c0bff3065f8be70d83ae26514ae60 Mon Sep 17 00:00:00 2001 From: gissehel Date: Fri, 19 Apr 2013 19:58:36 +0200 Subject: [PATCH 2/3] I don't think that printing on stdout is the right thing to do in this api layer. It should be handled by the calling layer shounldn't it ? Was that debug code ? --- mega/mega.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mega/mega.py b/mega/mega.py index 4d68a52..624aef6 100644 --- a/mega/mega.py +++ b/mega/mega.py @@ -402,9 +402,9 @@ class Mega(object): attribs = decrypt_attr(attribs, k) file_name = attribs['n'] - print("downloading {0} (size: {1}), url = {2}".format(attribs['n'].encode("utf8"), - file_size, - file_url)) + # print("downloading {0} (size: {1}), url = {2}".format(attribs['n'].encode("utf8"), + # file_size, + # file_url)) input_file = requests.get(file_url, stream=True).raw From 89a40306a2ae7ec08b954bec8be3010e4e73062e Mon Sep 17 00:00:00 2001 From: gissehel Date: Fri, 19 Apr 2013 20:10:52 +0200 Subject: [PATCH 3/3] Downloading using a tempory filename is safer. The tempory filename provide enouth information to show who (megapy) when and what is downloaded --- mega/mega.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mega/mega.py b/mega/mega.py index 624aef6..ed539df 100644 --- a/mega/mega.py +++ b/mega/mega.py @@ -7,6 +7,8 @@ import os import random import binascii import requests +import time +import shutil from .errors import ValidationError, RequestError from .crypto import * @@ -401,6 +403,7 @@ class Mega(object): attribs = base64_url_decode(file_data['at']) attribs = decrypt_attr(attribs, k) file_name = attribs['n'] + file_name_tmp = '.megapy-%s-%s' % (int(time.time()*1000), filename) # print("downloading {0} (size: {1}), url = {2}".format(attribs['n'].encode("utf8"), # file_size, @@ -411,7 +414,7 @@ class Mega(object): if dest_path: output_file = open(dest_path + '/' + file_name, 'wb') else: - output_file = open(file_name, 'wb') + output_file = open(file_name_tmp, 'wb') counter = Counter.new( 128, initial_value=((iv[0] << 32) + iv[1]) << 64) @@ -448,6 +451,8 @@ class Mega(object): if (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]) != meta_mac: raise ValueError('Mismatched mac') + shutil.move(file_name_tmp, file_name) + ########################################################################## # UPLOAD def upload(self, filename, dest=None):