Tidy up TelegramBareClient.download_file CDN's mess

This commit is contained in:
Lonami Exo 2017-08-24 18:00:27 +02:00
parent d5323c5506
commit 4384ccb132
2 changed files with 51 additions and 38 deletions

View File

@ -458,7 +458,6 @@ class TelegramBareClient:
takes two parameters, (bytes_downloaded, total_bytes). Note that takes two parameters, (bytes_downloaded, total_bytes). Note that
'total_bytes' simply equals 'file_size', and may be None. 'total_bytes' simply equals 'file_size', and may be None.
""" """
# TODO Clean up the CDN mess below
if not part_size_kb: if not part_size_kb:
if not file_size: if not file_size:
part_size_kb = 64 # Reasonable default part_size_kb = 64 # Reasonable default
@ -487,49 +486,33 @@ class TelegramBareClient:
try: try:
offset_index = 0 offset_index = 0
cdn_redirect = None cdn_file_token = None
cdn_aes = None
def encrypt_method(x):
return x # Defaults to no-op
while True: while True:
offset = offset_index * part_size offset = offset_index * part_size
try: try:
if cdn_redirect: if cdn_file_token:
result = client(GetCdnFileRequest( result = client(GetCdnFileRequest(
cdn_redirect.file_token, offset, part_size cdn_file_token, offset, part_size
)) ))
else: else:
result = client(GetFileRequest( result = client(GetFileRequest(
input_location, offset, part_size input_location, offset, part_size
)) ))
if isinstance(result, FileCdnRedirect): if isinstance(result, FileCdnRedirect):
# https://core.telegram.org/cdn client, cdn_file_token, encrypt_method, result = \
cdn_redirect = result self._prepare_cdn_redirect(
cdn_aes = pyaes.AESModeOfOperationCTR( result, offset, part_size
result.encryption_key )
)
# The returned IV is the counter used on CTR
cdn_aes._counter._counter = list(
result.encryption_iv[:12] +
(offset >> 4).to_bytes(4, 'big')
)
client, cdn_file = self._get_cdn_client( if result is None:
result.dc_id, # File was not ready on the CDN yet
GetCdnFileRequest( continue
cdn_redirect.file_token, offset, part_size
)
)
if isinstance(cdn_file, CdnFileReuploadNeeded):
# We need to use the original client here
self(ReuploadCdnFileRequest(
file_token=cdn_redirect.file_token,
request_token=cdn_file.request_token
))
# TODO else: we have the first file bytes already,
# avoid a redundant call
continue
except FileMigrateError as e: except FileMigrateError as e:
client = self._get_exported_client(e.new_dc) client = self._get_exported_client(e.new_dc)
@ -543,16 +526,42 @@ class TelegramBareClient:
# Return some extra information, unless it's a cdn file # Return some extra information, unless it's a cdn file
return getattr(result, 'type', '') return getattr(result, 'type', '')
if cdn_redirect: f.write(encrypt_method(result.bytes))
# We first need to decrypt the result
# TODO Use libssl if available
result.bytes = cdn_aes.encrypt(result.bytes)
f.write(result.bytes)
if progress_callback: if progress_callback:
progress_callback(f.tell(), file_size) progress_callback(f.tell(), file_size)
finally: finally:
if isinstance(file, str): if isinstance(file, str):
f.close() f.close()
def _prepare_cdn_redirect(self, cdn_redirect, offset, part_size):
"""Returns (client, cdn_file_token, encrypt_method, result)"""
# https://core.telegram.org/cdn
# TODO Use libssl if available
cdn_aes = pyaes.AESModeOfOperationCTR(
cdn_redirect.encryption_key
)
# The returned IV is the counter used on CTR
cdn_aes._counter._counter = list(
cdn_redirect.encryption_iv[:12] +
(offset >> 4).to_bytes(4, 'big')
)
client, cdn_file = self._get_cdn_client(
cdn_redirect.dc_id,
GetCdnFileRequest(
cdn_redirect.file_token, offset, part_size
)
)
if isinstance(cdn_file, CdnFileReuploadNeeded):
# We need to use the original client here
self(ReuploadCdnFileRequest(
file_token=cdn_redirect.file_token,
request_token=cdn_file.request_token
))
return client, cdn_redirect.file_token, cdn_aes.encrypt, None
else:
# We have the first bytes for the file
return client, cdn_redirect.file_token, cdn_aes.encrypt, cdn_file
# endregion # endregion

View File

@ -39,6 +39,10 @@ class HigherLevelTests(unittest.TestCase):
client.download_media(msg, out) client.download_media(msg, out)
assert sha256(data).digest() == sha256(out.getvalue()).digest() assert sha256(data).digest() == sha256(out.getvalue()).digest()
out = BytesIO()
client.download_media(msg, out) # Won't redirect
assert sha256(data).digest() == sha256(out.getvalue()).digest()
client.log_out() client.log_out()
finally: finally:
client.disconnect() client.disconnect()