def download(fid, secret, dest, password=None, url=None): metadata, nonce = get_metadata(fid, secret, password, url) encryptKey = deriveFileKey(secret) authKey = deriveAuthKey(secret, password, url) sig = hmac.new(authKey, nonce, sha256).digest() url = "https://send.firefox.com/api/download/" + fid resp = requests.get(url, headers={'Authorization': 'send-v1 ' + b64encode(sig)}, stream=True) resp.raise_for_status() flen = int(resp.headers.get('Content-Length')) filename = metadata['metadata']['name'] if os.path.isdir(dest): filename = os.path.join(dest, filename) else: filename = dest iv = b64decode(metadata['metadata']['iv']) cipher = AES.new(encryptKey, AES.MODE_GCM, iv, mac_len=16) ho = sha256() print("Downloading to %s..." % filename) try: with open(filename + '.tmp', 'wb') as outf: bar = ProgressBar(expected_size=flen, filled_char='=') dl = 0 tag = b'' taglen = 16 for data in resp.iter_content(chunk_size=8192): dl += len(data) bar.show(dl) if dl > flen - taglen: dend = max(len(data) - (dl - (flen - taglen)), 0) tag += data[dend:] data = data[:dend] chunk = cipher.decrypt(data) ho.update(chunk) outf.write(chunk) if len(tag) == taglen: break print() cipher.verify(tag) except Exception as e: print("File download failed:", e) os.unlink(filename + '.tmp') else: os.rename(filename + '.tmp', filename) print("Done, file verified!")
def __init__(self, stream=None, expected_size=None, chunk_size=512): """ :param stream: Streaming API response object returned by the 'requests' library :type: requests.Response :param int expected_size: Total size of file being downloaded (in bytes) :param int chunk_size: Chunk size to use when iterating over streamed response content """ self.stream = stream self.prog_bar = ProgressBar(expected_size=expected_size, filled_char='=') self.progress = 0 self.chunk_size = chunk_size
def __call__(self, size_rd, total): if self.bar is None: self.bar = ProgressBar(expected_size=total, filled_char='=') self.bar.show(size_rd)