Spaces:
Sleeping
Sleeping
Download app.py from gabefromsimplebit/SimpleBit-Signer: direct link, hf CLI and curl.
- Browser
- Download file 7.34 kB
-
https://huggingface.co/spaces/gabefromsimplebit/SimpleBit-Signer/resolve/main/app.py
- Command line
-
hf download hf://spaces/gabefromsimplebit/SimpleBit-Signer/app.py
-
curl -L -o app.py https://huggingface.co/spaces/gabefromsimplebit/SimpleBit-Signer/resolve/main/app.py
7.34 kB
| from flask import Flask, request, send_file, jsonify, redirect | |
| from flask_cors import CORS | |
| import subprocess | |
| import plistlib | |
| import zipfile | |
| import shutil | |
| import uuid | |
| import tempfile | |
| from pathlib import Path | |
| app = Flask(__name__) | |
| CORS(app) | |
| BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space" | |
| # ------------------------------------------------- | |
| # HOME | |
| # ------------------------------------------------- | |
| def home(): | |
| return jsonify({"status": "online"}) | |
| # ------------------------------------------------- | |
| # FIND .APP BUNDLE | |
| # ------------------------------------------------- | |
| def find_app_bundle(temp_dir): | |
| for item in Path(temp_dir).rglob("*.app"): | |
| if item.is_dir(): | |
| return item | |
| return None | |
| # ------------------------------------------------- | |
| # READ INFO.PLIST | |
| # ------------------------------------------------- | |
| def read_info_plist(app_bundle): | |
| for f in Path(app_bundle).rglob("Info.plist"): | |
| with open(f, "rb") as fp: | |
| return f, plistlib.load(fp) | |
| return None, None | |
| def write_info_plist(plist_path, data): | |
| with open(plist_path, "wb") as fp: | |
| plistlib.dump(data, fp) | |
| # ------------------------------------------------- | |
| # SIGN ROUTE | |
| # ------------------------------------------------- | |
| def sign(): | |
| try: | |
| if "ipa" not in request.files: | |
| return jsonify({"error": "Missing IPA"}), 400 | |
| if "p12" not in request.files: | |
| return jsonify({"error": "Missing P12"}), 400 | |
| if "provision" not in request.files: | |
| return jsonify({"error": "Missing provisioning profile"}), 400 | |
| zsign_path = shutil.which("zsign") | |
| if not zsign_path: | |
| return jsonify({"error": "zsign not installed"}), 500 | |
| job_id = str(uuid.uuid4())[:8] | |
| job_dir = Path(job_id) | |
| job_dir.mkdir(exist_ok=True) | |
| ipa_path = job_dir / "app.ipa" | |
| p12_path = job_dir / "cert.p12" | |
| prov_path = job_dir / "profile.mobileprovision" | |
| signed_path = job_dir / "signed.ipa" | |
| request.files["ipa"].save(ipa_path) | |
| request.files["p12"].save(p12_path) | |
| request.files["provision"].save(prov_path) | |
| password = request.form.get("password", "") | |
| # ------------------------------------------------- | |
| # EXTRACT IPA | |
| # ------------------------------------------------- | |
| temp_dir = tempfile.mkdtemp() | |
| with zipfile.ZipFile(ipa_path, "r") as zip_ref: | |
| zip_ref.extractall(temp_dir) | |
| app_bundle = find_app_bundle(temp_dir) | |
| if not app_bundle: | |
| shutil.rmtree(temp_dir) | |
| return jsonify({"error": "No .app found"}), 500 | |
| # ------------------------------------------------- | |
| # READ ORIGINAL INFO | |
| # ------------------------------------------------- | |
| plist_path, plist_data = read_info_plist(app_bundle) | |
| if not plist_data: | |
| shutil.rmtree(temp_dir) | |
| return jsonify({"error": "Info.plist missing"}), 500 | |
| original_bundle_id = plist_data.get("CFBundleIdentifier", "unknown.bundle") | |
| original_name = ( | |
| plist_data.get("CFBundleDisplayName") | |
| or plist_data.get("CFBundleName") | |
| or "App" | |
| ) | |
| # ------------------------------------------------- | |
| # DO NOT CHANGE BUNDLE ID (ENTERPRISE MODE) | |
| # ------------------------------------------------- | |
| target_bundle_id = original_bundle_id | |
| plist_data["CFBundleIdentifier"] = target_bundle_id | |
| plist_data["CFBundleDisplayName"] = original_name | |
| plist_data["CFBundleName"] = original_name | |
| write_info_plist(plist_path, plist_data) | |
| # ------------------------------------------------- | |
| # SIGN | |
| # ------------------------------------------------- | |
| command = [ | |
| zsign_path, | |
| "-f", | |
| "-k", str(p12_path), | |
| "-p", password, | |
| "-m", str(prov_path), | |
| str(app_bundle) | |
| ] | |
| result = subprocess.run(command, capture_output=True, text=True) | |
| if result.returncode != 0: | |
| shutil.rmtree(temp_dir) | |
| return jsonify({ | |
| "error": "Signing failed", | |
| "stdout": result.stdout, | |
| "stderr": result.stderr | |
| }), 500 | |
| # ------------------------------------------------- | |
| # BUILD CLEAN IPA | |
| # ------------------------------------------------- | |
| payload_dir = Path(job_dir) / "Payload" | |
| payload_dir.mkdir(exist_ok=True) | |
| shutil.copytree(app_bundle, payload_dir / app_bundle.name) | |
| zip_path = Path(job_dir) / "signed" | |
| shutil.make_archive(str(zip_path), "zip", job_dir, "Payload") | |
| shutil.move(str(zip_path) + ".zip", signed_path) | |
| shutil.rmtree(temp_dir) | |
| # save ORIGINAL bundle id for manifest | |
| (job_dir / "bundle_id.txt").write_text(original_bundle_id) | |
| plist_url = f"{BASE_URL}/plist/{job_id}" | |
| install_url = f"itms-services://?action=download-manifest&url={plist_url}" | |
| return redirect(install_url) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 500 | |
| # ------------------------------------------------- | |
| # MANIFEST | |
| # ------------------------------------------------- | |
| def plist(job_id): | |
| job_dir = Path(job_id) | |
| bundle_id = "unknown" | |
| if (job_dir / "bundle_id.txt").exists(): | |
| bundle_id = (job_dir / "bundle_id.txt").read_text().strip() | |
| ipa_url = f"{BASE_URL}/download/{job_id}" | |
| plist_content = f"""<?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" | |
| "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>items</key> | |
| <array> | |
| <dict> | |
| <key>assets</key> | |
| <array> | |
| <dict> | |
| <key>kind</key> | |
| <string>software-package</string> | |
| <key>url</key> | |
| <string>{ipa_url}</string> | |
| </dict> | |
| </array> | |
| <key>metadata</key> | |
| <dict> | |
| <key>bundle-identifier</key> | |
| <string>{bundle_id}</string> | |
| <key>bundle-version</key> | |
| <string>1.0</string> | |
| <key>kind</key> | |
| <string>software</string> | |
| <key>title</key> | |
| <string>Signed App</string> | |
| </dict> | |
| </dict> | |
| </array> | |
| </dict> | |
| </plist> | |
| """ | |
| plist_path = job_dir / "manifest.plist" | |
| plist_path.write_text(plist_content) | |
| return send_file(plist_path, mimetype="application/xml") | |
| # ------------------------------------------------- | |
| # DOWNLOAD IPA | |
| # ------------------------------------------------- | |
| def download(job_id): | |
| ipa_path = Path(job_id) / "signed.ipa" | |
| if not ipa_path.exists(): | |
| return jsonify({"error": "IPA not found"}), 404 | |
| response = send_file(ipa_path, mimetype="application/octet-stream") | |
| response.headers["Content-Disposition"] = "inline; filename=signed.ipa" | |
| return response | |
| # ------------------------------------------------- | |
| # HEALTH | |
| # ------------------------------------------------- | |
| def health(): | |
| return jsonify({"ok": True, "zsign": shutil.which("zsign") is not None}) | |
| # ------------------------------------------------- | |
| # RUN | |
| # ------------------------------------------------- | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=7860, debug=True) |