#!/usr/bin/env python3 # Verifies the SHA-256 hash of a downloaded file from (https://gitflic.ru/project/magnolia1234/bpc_uploads) against the hash in [release-hashes.txt](https://gitflic.ru/project/magnolia1234/bpc_uploads/blob/raw?file=release-hashes.txt). # 1. Downloads the hash file from the [HASH_URL] and extracts the expected hash for the correspondent [FILENAME]. # 2. Downloads the zip-file [FILENAME] from [DOWNLOAD_URL] and calculates its SHA-256 hash. # 3. Compares the calculated hash with the expected hash. # 4. On match, prompts the user to move the file to a permanent location [DOWNLOAD_DIR]. # 5. Moves and extracts the file if confirmed; otherwise, deletes the temporary file. # Note: Before running script, replace [DOWNLOAD_DIR] with permanent location on your computer that you use for your extension, excluding the extracted folder name [EXTRACT_DIR_NAME]. import requests import hashlib import re import tempfile import os import shutil import zipfile import json # Constants HASH_URL = "https://gitflic.ru/project/magnolia1234/bpc_uploads/blob/raw?file=release-hashes.txt" FILENAME = "bypass-paywalls-chrome-clean-master.zip" DOWNLOAD_URL = f"https://gitflic.ru/project/magnolia1234/bpc_uploads/blob/raw?file={FILENAME}" DOWNLOAD_DIR = os.path.expanduser("~/folder/macsetup/_packages/bpc-gitflic") # Replace with permanent location EXTRACT_DIR_NAME = "bypass-paywalls-chrome-clean-master" # Extracted folder name within the zip file EXTRACT_DIR_PATH = os.path.join(DOWNLOAD_DIR, EXTRACT_DIR_NAME) COLOR_YELLOW = "\033[0;33m" COLOR_RESET = "\033[0m" def fetch_hash_file(url): response = requests.get(url) response.raise_for_status() return response.text def extract_hash(content, filename): # Regex to find the hash block for the specified filename pattern = re.compile( rf'Filename\s+:\s+{re.escape(filename)}.*?SHA-256\s+:\s+(\S+)', re.DOTALL ) match = pattern.search(content) if match: return match.group(1) raise ValueError(f"Hash for {filename} not found in release hashes.") def download_file(url, local_path): response = requests.get(url, stream=True) response.raise_for_status() with open(local_path, 'wb') as file: for chunk in response.iter_content(chunk_size=8192): file.write(chunk) def compute_sha256(file_path): sha256 = hashlib.sha256() with open(file_path, 'rb') as file: while chunk := file.read(8192): sha256.update(chunk) return sha256.hexdigest() def extract_zip(zip_path, extract_to): with zipfile.ZipFile(zip_path, 'r') as zip_ref: # Extract to a temporary directory to handle extraction temp_extract_dir = os.path.join(DOWNLOAD_DIR, 'temp_extract') os.makedirs(temp_extract_dir, exist_ok=True) zip_ref.extractall(temp_extract_dir) # Move extracted folder to the final location extracted_folder_path = os.path.join(temp_extract_dir, EXTRACT_DIR_NAME) if os.path.isdir(extracted_folder_path): replace_existing_folder(extract_to) shutil.move(extracted_folder_path, extract_to) else: raise ValueError(f"Expected folder {EXTRACT_DIR_NAME} not found in ZIP file.") shutil.rmtree(temp_extract_dir) # Clean up the temporary extraction directory def replace_existing_folder(path): if os.path.exists(path): print(f"Removing existing folder: {path}") shutil.rmtree(path) def read_version_from_manifest(path): manifest_path = os.path.join(path, 'manifest.json') with open(manifest_path, 'r') as file: manifest = json.load(file) return manifest.get('version', 'unknown') def main(): # Fetch the release hashes file print("Fetching release hashes...") hash_content = fetch_hash_file(HASH_URL) # Extract the expected SHA-256 hash for the specific filename print(f"Extracting SHA-256 hash for {FILENAME}...") expected_hash = extract_hash(hash_content, FILENAME) print(f"Extracted SHA-256 hash: {expected_hash}") # Create temporary files with tempfile.NamedTemporaryFile(delete=False) as temp_file: download_file(DOWNLOAD_URL, temp_file.name) temp_file_path = temp_file.name print(f"File downloaded to: {temp_file_path}") # Compute the SHA-256 hash of the downloaded file print("Computing SHA256 hash...") computed_hash = compute_sha256(temp_file_path) # Compare hashes if computed_hash == expected_hash: print(f"Hash match: {computed_hash}") # Read current version from existing manifest old_version = 'unknown' if os.path.exists(EXTRACT_DIR_PATH): old_version = read_version_from_manifest(EXTRACT_DIR_PATH) print(f"Current version: {old_version}") # Extract the new version from the ZIP file with zipfile.ZipFile(temp_file_path, 'r') as zip_ref: with zip_ref.open(os.path.join(EXTRACT_DIR_NAME, 'manifest.json')) as manifest_file: new_manifest = json.load(manifest_file) new_version = new_manifest.get('version', 'unknown') print(f"Latest version: {new_version}") if old_version == new_version: print("The extension is up to date.") os.remove(temp_file_path) else: # Ask the user if they want to move the file to the permanent location user_input = input("Do you want to move the file to the permanent location? (y/n): ").strip().lower() if user_input == 'y': permanent_location = os.path.join(DOWNLOAD_DIR, FILENAME) os.makedirs(DOWNLOAD_DIR, exist_ok=True) if os.path.exists(permanent_location): print(f"File already exists at {permanent_location}. Replacing it.") shutil.move(temp_file_path, permanent_location) print(f"File moved to: {permanent_location}") # Extract the ZIP file print("Extracting ZIP file...") extract_zip(permanent_location, EXTRACT_DIR_PATH) print(f"Files extracted to: {EXTRACT_DIR_PATH}") print(f"{COLOR_YELLOW}Updated {old_version} to {new_version}{COLOR_RESET}") print(f"{COLOR_YELLOW}Restart your browser for changes to take effect.{COLOR_RESET}") else: print("File not moved.") os.remove(temp_file_path) else: print(f"Hash mismatch: computed {computed_hash}, expected {expected_hash}") os.remove(temp_file_path) if __name__ == "__main__": main()