gabefromsimplebit commited on
Commit
7107715
·
verified ·
1 Parent(s): b6b9155

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +128 -73
app.py CHANGED
@@ -1,102 +1,138 @@
1
- from flask import Flask, request, send_file, redirect
2
  from flask_cors import CORS
3
  import subprocess
4
  import os
5
  import uuid
6
- import re
 
 
7
 
8
  app = Flask(__name__)
9
- CORS(app)
10
 
11
- # Base URL for your Hugging Face Space
12
  BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space"
13
 
14
- @app.route('/')
 
15
  def home():
16
  return "SimpleBit Signer is Online!"
17
 
 
18
  def get_actual_bundle_id(ipa_path):
19
- """ Extracts the actual Bundle ID from the IPA's Info.plist """
 
 
20
  try:
21
- # Use unzip to peek at the Info.plist and grep for the ID
22
- # This is more reliable than parsing zsign logs
23
- cmd = f"unzip -p {ipa_path} Payload/*.app/Info.plist | grep -A1 'CFBundleIdentifier' | grep 'string' | sed 's/<[^>]*>//g' | str.strip()"
24
- # Using a more robust subprocess call for the same logic:
25
- result = subprocess.run(
26
- f"unzip -p {ipa_path} Payload/*.app/Info.plist | grep -A1 'CFBundleIdentifier'",
27
- shell=True, capture_output=True, text=True
28
- )
29
- match = re.search(r"<string>(.*)</string>", result.stdout)
30
- if match:
31
- return match.group(1).strip()
32
- except Exception:
33
- pass
 
34
  return None
35
 
36
- @app.route('/sign', methods=['POST'])
 
37
  def sign():
38
- job_id = str(uuid.uuid4())[:8]
39
- os.makedirs(job_id, exist_ok=True)
40
-
41
- ipa = request.files['ipa']
42
- p12 = request.files['p12']
43
- prov = request.files['provision']
44
- password = request.form.get('password', '1234')
45
-
46
- ipa_path = os.path.join(job_id, "app.ipa")
47
- p12_path = os.path.join(job_id, "cert.p12")
48
- prov_path = os.path.join(job_id, "prov.mobileprovision")
49
- out_path = os.path.join(job_id, "signed.ipa")
50
-
51
- ipa.save(ipa_path)
52
- p12.save(p12_path)
53
- prov.save(prov_path)
54
-
55
- # STEP 1: Get the REAL Bundle ID first
56
- bundle_id = get_actual_bundle_id(ipa_path)
57
-
58
- # STEP 2: Sign the app
59
- command = [
60
- "zsign",
61
- "-k", p12_path,
62
- "-p", password,
63
- "-m", prov_path,
64
- "-o", out_path,
65
- "-z", "0",
66
- ipa_path
67
- ]
68
-
69
  try:
70
- result = subprocess.run(command, capture_output=True, text=True, check=True)
71
-
72
- # If extraction failed earlier, try one last time from zsign logs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  if not bundle_id:
74
- combined_output = result.stdout + result.stderr
75
- match = re.search(r"Bundle\s?Id:\s+([a-zA-Z0-9\.-]+)", combined_output, re.IGNORECASE)
76
- bundle_id = match.group(1).strip() if match else "com.simplebit.dynamic"
77
 
78
- # Save the confirmed ID for the /plist route
79
- with open(os.path.join(job_id, "bundle_id.txt"), "w") as f:
80
  f.write(bundle_id)
81
 
82
  plist_url = f"{BASE_URL}/plist/{job_id}"
83
- return redirect(f"itms-services://?action=download-manifest&url={plist_url}")
84
-
85
- except subprocess.CalledProcessError as e:
86
- return f"Signing failed: {e.stderr}", 500
87
 
88
- @app.route('/plist/<job_id>')
 
 
 
 
 
 
 
 
 
 
89
  def serve_plist(job_id):
 
 
90
  try:
91
- with open(os.path.join(job_id, "bundle_id.txt"), "r") as f:
92
  bundle_id = f.read().strip()
93
  except:
94
  bundle_id = "com.simplebit.dynamic"
95
 
96
  ipa_direct_url = f"{BASE_URL}/download/{job_id}"
97
-
98
  plist_content = f"""<?xml version="1.0" encoding="UTF-8"?>
99
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 
100
  <plist version="1.0">
101
  <dict>
102
  <key>items</key>
@@ -111,14 +147,18 @@ def serve_plist(job_id):
111
  <string>{ipa_direct_url}</string>
112
  </dict>
113
  </array>
 
114
  <key>metadata</key>
115
  <dict>
116
  <key>bundle-identifier</key>
117
  <string>{bundle_id}</string>
 
118
  <key>bundle-version</key>
119
  <string>1.0</string>
 
120
  <key>kind</key>
121
  <string>software</string>
 
122
  <key>title</key>
123
  <string>SimpleBit Signed App</string>
124
  </dict>
@@ -126,16 +166,31 @@ def serve_plist(job_id):
126
  </array>
127
  </dict>
128
  </plist>"""
129
-
130
- plist_path = os.path.join(job_id, "manifest.plist")
 
131
  with open(plist_path, "w") as f:
132
  f.write(plist_content)
133
-
134
- return send_file(plist_path, mimetype='text/xml')
135
 
136
- @app.route('/download/<job_id>')
 
 
 
 
 
 
137
  def download_ipa(job_id):
138
- return send_file(os.path.join(job_id, "signed.ipa"), as_attachment=True)
 
 
 
 
 
 
 
 
 
 
139
 
140
  if __name__ == "__main__":
141
  app.run(host="0.0.0.0", port=7860)
 
1
+ from flask import Flask, request, send_file, redirect, jsonify
2
  from flask_cors import CORS
3
  import subprocess
4
  import os
5
  import uuid
6
+ import plistlib
7
+ import zipfile
8
+ from pathlib import Path
9
 
10
  app = Flask(__name__)
11
+ CORS(app)
12
 
 
13
  BASE_URL = "https://gabefromsimplebit-simplebit-signer.hf.space"
14
 
15
+
16
+ @app.route("/")
17
  def home():
18
  return "SimpleBit Signer is Online!"
19
 
20
+
21
  def get_actual_bundle_id(ipa_path):
22
+ """
23
+ Extract the real bundle ID from the IPA Info.plist safely.
24
+ """
25
  try:
26
+ with zipfile.ZipFile(ipa_path, "r") as ipa:
27
+ plist_file = next(
28
+ f for f in ipa.namelist()
29
+ if f.startswith("Payload/") and f.endswith("Info.plist")
30
+ )
31
+
32
+ with ipa.open(plist_file) as fp:
33
+ plist_data = plistlib.load(fp)
34
+
35
+ return plist_data.get("CFBundleIdentifier")
36
+
37
+ except Exception as e:
38
+ print(f"Bundle ID extraction failed: {e}")
39
+
40
  return None
41
 
42
+
43
+ @app.route("/sign", methods=["POST"])
44
  def sign():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  try:
46
+ # Validate uploads
47
+ if "ipa" not in request.files:
48
+ return jsonify({"error": "Missing IPA file"}), 400
49
+
50
+ if "p12" not in request.files:
51
+ return jsonify({"error": "Missing P12 certificate"}), 400
52
+
53
+ if "provision" not in request.files:
54
+ return jsonify({"error": "Missing mobileprovision file"}), 400
55
+
56
+ job_id = str(uuid.uuid4())[:8]
57
+ job_dir = Path(job_id)
58
+ job_dir.mkdir(exist_ok=True)
59
+
60
+ ipa = request.files["ipa"]
61
+ p12 = request.files["p12"]
62
+ prov = request.files["provision"]
63
+
64
+ password = request.form.get("password", "1234")
65
+
66
+ ipa_path = job_dir / "app.ipa"
67
+ p12_path = job_dir / "cert.p12"
68
+ prov_path = job_dir / "prov.mobileprovision"
69
+ out_path = job_dir / "signed.ipa"
70
+
71
+ ipa.save(ipa_path)
72
+ p12.save(p12_path)
73
+ prov.save(prov_path)
74
+
75
+ # Extract bundle ID before signing
76
+ bundle_id = get_actual_bundle_id(ipa_path)
77
+
78
+ # Run zsign
79
+ command = [
80
+ "zsign",
81
+ "-k", str(p12_path),
82
+ "-p", password,
83
+ "-m", str(prov_path),
84
+ "-o", str(out_path),
85
+ "-z", "0",
86
+ str(ipa_path)
87
+ ]
88
+
89
+ result = subprocess.run(
90
+ command,
91
+ capture_output=True,
92
+ text=True
93
+ )
94
+
95
+ if result.returncode != 0:
96
+ return jsonify({
97
+ "error": "Signing failed",
98
+ "details": result.stderr
99
+ }), 500
100
+
101
+ # Fallback bundle ID
102
  if not bundle_id:
103
+ bundle_id = "com.simplebit.dynamic"
 
 
104
 
105
+ # Save bundle ID
106
+ with open(job_dir / "bundle_id.txt", "w") as f:
107
  f.write(bundle_id)
108
 
109
  plist_url = f"{BASE_URL}/plist/{job_id}"
 
 
 
 
110
 
111
+ return redirect(
112
+ f"itms-services://?action=download-manifest&url={plist_url}"
113
+ )
114
+
115
+ except Exception as e:
116
+ return jsonify({
117
+ "error": str(e)
118
+ }), 500
119
+
120
+
121
+ @app.route("/plist/<job_id>")
122
  def serve_plist(job_id):
123
+ job_dir = Path(job_id)
124
+
125
  try:
126
+ with open(job_dir / "bundle_id.txt", "r") as f:
127
  bundle_id = f.read().strip()
128
  except:
129
  bundle_id = "com.simplebit.dynamic"
130
 
131
  ipa_direct_url = f"{BASE_URL}/download/{job_id}"
132
+
133
  plist_content = f"""<?xml version="1.0" encoding="UTF-8"?>
134
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
135
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
136
  <plist version="1.0">
137
  <dict>
138
  <key>items</key>
 
147
  <string>{ipa_direct_url}</string>
148
  </dict>
149
  </array>
150
+
151
  <key>metadata</key>
152
  <dict>
153
  <key>bundle-identifier</key>
154
  <string>{bundle_id}</string>
155
+
156
  <key>bundle-version</key>
157
  <string>1.0</string>
158
+
159
  <key>kind</key>
160
  <string>software</string>
161
+
162
  <key>title</key>
163
  <string>SimpleBit Signed App</string>
164
  </dict>
 
166
  </array>
167
  </dict>
168
  </plist>"""
169
+
170
+ plist_path = job_dir / "manifest.plist"
171
+
172
  with open(plist_path, "w") as f:
173
  f.write(plist_content)
 
 
174
 
175
+ return send_file(
176
+ plist_path,
177
+ mimetype="application/xml"
178
+ )
179
+
180
+
181
+ @app.route("/download/<job_id>")
182
  def download_ipa(job_id):
183
+ ipa_path = Path(job_id) / "signed.ipa"
184
+
185
+ if not ipa_path.exists():
186
+ return jsonify({"error": "IPA not found"}), 404
187
+
188
+ return send_file(
189
+ ipa_path,
190
+ as_attachment=True,
191
+ download_name="signed.ipa"
192
+ )
193
+
194
 
195
  if __name__ == "__main__":
196
  app.run(host="0.0.0.0", port=7860)