python 3 support

master
Richard O'Dwyer 2019-10-16 21:59:31 +01:00
parent 2cb2148ddc
commit 321ccbfa7f
4 changed files with 49 additions and 28 deletions

View File

@ -27,16 +27,16 @@ def test():
files = m.get_files() files = m.get_files()
# get account disk quota in MB # get account disk quota in MB
print(m.get_quota()) print((m.get_quota()))
# get account storage space # get account storage space
print(m.get_storage_space()) print((m.get_storage_space()))
# example iterate over files # example iterate over files
for file in files: for file in files:
print(files[file]) print((files[file]))
# upload file # upload file
print(m.upload('examples.py')) print((m.upload('examples.py')))
# search for a file in account # search for a file in account
file = m.find('examples.py') file = m.find('examples.py')
@ -47,17 +47,17 @@ def test():
print(link) print(link)
# download file. by file object or url # download file. by file object or url
print m.download(file, '/tmp') print(m.download(file, '/tmp'))
# m.download_url(link) # m.download_url(link)
# delete or destroy file. by id or url # delete or destroy file. by id or url
print(m.delete(file[0])) print((m.delete(file[0])))
# print(m.destroy(file[0])) # print(m.destroy(file[0]))
# print(m.delete_url(link)) # print(m.delete_url(link))
# print(m.destroy_url(link)) # print(m.destroy_url(link))
# empty trash # empty trash
print(m.empty_trash()) print((m.empty_trash()))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -4,15 +4,33 @@ import base64
import struct import struct
import binascii import binascii
import random import random
import sys
### Python3 compatibility
if sys.version_info < (3, ):
def makebyte(x):
return x
def makestring(x):
return x
else:
import codecs
def makebyte(x):
return codecs.latin_1_encode(x)[0]
def makestring(x):
return codecs.latin_1_decode(x)[0]
def aes_cbc_encrypt(data, key): def aes_cbc_encrypt(data, key):
aes_cipher = AES.new(key, AES.MODE_CBC, '\0' * 16) aes_cipher = AES.new(key, AES.MODE_CBC, makebyte('\0' * 16))
return aes_cipher.encrypt(data) return aes_cipher.encrypt(data)
def aes_cbc_decrypt(data, key): def aes_cbc_decrypt(data, key):
aes_cipher = AES.new(key, AES.MODE_CBC, '\0' * 16) aes_cipher = AES.new(key, AES.MODE_CBC, makebyte('\0' * 16))
return aes_cipher.decrypt(data) return aes_cipher.decrypt(data)
@ -59,14 +77,16 @@ def decrypt_key(a, key):
def encrypt_attr(attr, key): def encrypt_attr(attr, key):
attr = 'MEGA' + json.dumps(attr) attr = makebyte('MEGA' + json.dumps(attr))
if len(attr) % 16: if len(attr) % 16:
attr += '\0' * (16 - len(attr) % 16) attr += b'\0' * (16 - len(attr) % 16)
return aes_cbc_encrypt(attr, a32_to_str(key)) return aes_cbc_encrypt(attr, a32_to_str(key))
def decrypt_attr(attr, key): def decrypt_attr(attr, key):
attr = aes_cbc_decrypt(attr, a32_to_str(key)).rstrip('\0') attr = aes_cbc_decrypt(attr, a32_to_str(key))
attr = makestring(attr)
attr = attr.rstrip('\0')
return json.loads(attr[4:]) if attr[:6] == 'MEGA{"' else False return json.loads(attr[4:]) if attr[:6] == 'MEGA{"' else False
@ -75,9 +95,11 @@ def a32_to_str(a):
def str_to_a32(b): def str_to_a32(b):
if isinstance(b, str):
b = makebyte(b)
if len(b) % 4: if len(b) % 4:
# pad to multiple of 4 # pad to multiple of 4
b += '\0' * (4 - len(b) % 4) b += b'\0' * (4 - len(b) % 4)
return struct.unpack('>%dI' % (len(b) / 4), b) return struct.unpack('>%dI' % (len(b) / 4), b)
@ -98,6 +120,7 @@ def base64_to_a32(s):
def base64_url_encode(data): def base64_url_encode(data):
data = base64.b64encode(data) data = base64.b64encode(data)
data = makestring(data)
for search, replace in (('+', '-'), ('/', '_'), ('=', '')): for search, replace in (('+', '-'), ('/', '_'), ('=', '')):
data = data.replace(search, replace) data = data.replace(search, replace)
return data return data
@ -118,7 +141,6 @@ def get_chunks(size):
yield (p, size - p) yield (p, size - p)
# more general functions
def make_id(length): def make_id(length):
text = '' text = ''
possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

View File

@ -59,9 +59,7 @@ class Mega(object):
a32_to_base64(encrypt_key(master_key, password_key)), a32_to_base64(encrypt_key(master_key, password_key)),
'ts': 'ts':
base64_url_encode( base64_url_encode(
a32_to_str(session_self_challenge) + a32_to_str( a32_to_str(session_self_challenge) + a32_to_str(encrypt_key(session_self_challenge, master_key))
encrypt_key(session_self_challenge, master_key)
)
) )
} }
) )
@ -101,7 +99,7 @@ class Mega(object):
encrypted_sid = mpi_to_int(base64_url_decode(resp['csid'])) encrypted_sid = mpi_to_int(base64_url_decode(resp['csid']))
rsa_decrypter = RSA.construct( rsa_decrypter = RSA.construct(
( (
self.rsa_private_key[0] * self.rsa_private_key[1], 0L, self.rsa_private_key[0] * self.rsa_private_key[1], 0,
self.rsa_private_key[2], self.rsa_private_key[0], self.rsa_private_key[2], self.rsa_private_key[0],
self.rsa_private_key[1] self.rsa_private_key[1]
) )
@ -256,7 +254,7 @@ class Mega(object):
found = False found = False
for foldername in paths: for foldername in paths:
if foldername != '': if foldername != '':
for file in files.iteritems(): for file in files.items():
if file[1]['a'] and file[1]['t'] and \ if file[1]['a'] and file[1]['t'] and \
file[1]['a']['n'] == foldername: file[1]['a']['n'] == foldername:
if parent_desc == file[1]['p']: if parent_desc == file[1]['p']:
@ -273,7 +271,7 @@ class Mega(object):
Return file object from given filename Return file object from given filename
""" """
files = self.get_files() files = self.get_files()
for file in files.items(): for file in list(files.items()):
if not isinstance(file[1]['a'], dict): if not isinstance(file[1]['a'], dict):
continue continue
if file[1]['a'] and file[1]['a']['n'] == filename: if file[1]['a'] and file[1]['a']['n'] == filename:
@ -348,7 +346,7 @@ class Mega(object):
4: special trash bin 4: special trash bin
""" """
nodes = self.get_files() nodes = self.get_files()
for node in nodes.items(): for node in list(nodes.items()):
if node[1]['t'] == type: if node[1]['t'] == type:
return node return node
@ -385,7 +383,7 @@ class Mega(object):
node_id = None node_id = None
for i in node_data['f']: for i in node_data['f']:
if i['h'] != u'': if i['h'] != '':
node_id = i['h'] node_id = i['h']
return node_id return node_id
@ -608,11 +606,11 @@ class Mega(object):
if self.options.get('verbose') is True: if self.options.get('verbose') is True:
# temp file size # temp file size
file_info = os.stat(temp_output_file.name) file_info = os.stat(temp_output_file.name)
print( print((
'{0} of {1} downloaded'.format( '{0} of {1} downloaded'.format(
file_info.st_size, file_size file_info.st_size, file_size
) )
) ))
file_mac = str_to_a32(mac_str) file_mac = str_to_a32(mac_str)
@ -685,11 +683,11 @@ class Mega(object):
if self.options.get('verbose') is True: if self.options.get('verbose') is True:
# upload progress # upload progress
print( print((
'{0} of {1} uploaded'.format( '{0} of {1} uploaded'.format(
upload_progress, file_size upload_progress, file_size
) )
) ))
else: else:
output_file = requests.post( output_file = requests.post(
ul_url + "/0", data='', timeout=self.timeout ul_url + "/0", data='', timeout=self.timeout
@ -818,7 +816,7 @@ class Mega(object):
# determine target_node_id # determine target_node_id
if type(target) == int: if type(target) == int:
target_node_id = str(self.get_node_by_type(target)[0]) target_node_id = str(self.get_node_by_type(target)[0])
elif type(target) in (str, unicode): elif type(target) in (str, str):
target_node_id = target target_node_id = target
else: else:
file = target[1] file = target[1]

View File

@ -5,7 +5,8 @@ envlist = py{27,37}-normal,lint
commands = commands =
flake8 {toxinidir}/src/ flake8 {toxinidir}/src/
coverage erase coverage erase
PYTHONPATH=. pytest --cov=. {toxinidir}/tests/tests.py python setup.py install
pytest {toxinidir}/tests/tests.py
deps = deps =
-rrequirements-dev.txt -rrequirements-dev.txt