Skip to content

Commit 566067c

Browse files
committed
Save all signed versions instead of latest version & Separate tsschecker stuff into TSSChecker.java.
Also changes the background so it always saves all signed versions instead of only saving the versions that were previously unsaved.
1 parent 6f7575d commit 566067c

File tree

5 files changed

+354
-192
lines changed

5 files changed

+354
-192
lines changed

src/main/java/com/airsquared/blobsaver/Background.java

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import javafx.scene.control.ButtonType;
2525
import javafx.util.Duration;
2626
import org.json.JSONArray;
27-
import org.json.JSONObject;
2827

2928
import javax.imageio.ImageIO;
3029
import javax.swing.SwingUtilities;
@@ -37,16 +36,13 @@
3736
import java.awt.TrayIcon;
3837
import java.io.File;
3938
import java.io.IOException;
40-
import java.net.URL;
4139
import java.util.ArrayList;
4240
import java.util.Collections;
4341
import java.util.List;
44-
import java.util.Map;
4542
import java.util.concurrent.Executors;
4643
import java.util.concurrent.ScheduledExecutorService;
4744
import java.util.concurrent.TimeUnit;
4845
import java.util.prefs.Preferences;
49-
import java.util.stream.Collectors;
5046

5147
import static com.airsquared.blobsaver.Main.appPrefs;
5248
import static com.airsquared.blobsaver.Main.appVersion;
@@ -172,17 +168,16 @@ static void startBackground(boolean runOnlyOnce) {
172168
private static void saveBackgroundBlobs(int preset) {
173169
log("attempting to save for " + preset);
174170
Preferences presetPrefs = Preferences.userRoot().node("airsquared/blobsaver/preset" + preset);
175-
// presetPrefs.put("Saved Versions", "[]"); // for testing
176171
String identifier;
177172
if ("none".equals(presetPrefs.get("Device Model", ""))) {
178173
identifier = presetPrefs.get("Device Identifier", "");
179174
} else {
180175
identifier = textToIdentifier(presetPrefs.get("Device Model", ""));
181176
}
182177
log("identifier:" + identifier);
183-
String response;
178+
List<String> signedVersions;
184179
try {
185-
response = makeRequest(new URL("https://api.ipsw.me/v4/device/" + identifier));
180+
signedVersions = getAllSignedVersions(identifier);
186181
} catch (IOException e) {
187182
Notification notification = new Notification("Saving blobs failed", "Check your internet connection.\nIf it is working, click here to report this error.", Notification.ERROR_ICON);
188183
Notification.Notifier.INSTANCE.setPopupLifetime(Duration.minutes(1));
@@ -200,32 +195,12 @@ private static void saveBackgroundBlobs(int preset) {
200195
Notification.Notifier.INSTANCE.notify(notification);
201196
return;
202197
}
203-
log("made request");
204-
JSONArray firmwareListJson = new JSONObject(response).getJSONArray("firmwares");
205-
@SuppressWarnings("unchecked") List<Map<String, Object>> firmwareList = (List) firmwareListJson.toList();
206-
List<String> signedVersions = firmwareList.stream().filter(map -> Boolean.TRUE.equals(map.get("signed"))).map(map -> map.get("version").toString()).collect(Collectors.toList());
207198
log("signed versions:" + signedVersions);
208-
ArrayList<String> savedVersions = new ArrayList<>();
209-
JSONArray savedVersionsJson = new JSONArray(presetPrefs.get("Saved Versions", "[]"));
210-
for (int i = 0; i < savedVersionsJson.length(); i++) {
211-
savedVersions.add(savedVersionsJson.getString(i));
212-
}
213-
log("saved versions:" + savedVersions);
214-
ArrayList<String> versionsToSave = new ArrayList<>();
215-
signedVersions.forEach((version) -> {
216-
if (!savedVersions.contains(version)) {
217-
versionsToSave.add(version);
218-
}
219-
});
220-
log("versions to save:" + versionsToSave);
221-
if (versionsToSave.isEmpty()) {
222-
return;
223-
}
224199
String ecid = presetPrefs.get("ECID", "");
225200
String path = presetPrefs.get("Path", "");
226201
String boardConfig = presetPrefs.get("Board Config", "");
227202
String apnonce = presetPrefs.get("Apnonce", "");
228-
for (String version : versionsToSave) {
203+
for (String version : signedVersions) {
229204
File tsschecker;
230205
try {
231206
tsschecker = getTsschecker();
@@ -333,8 +308,6 @@ private static void saveBackgroundBlobs(int preset) {
333308
});
334309
Notification.Notifier.INSTANCE.notify(notification);
335310
}
336-
savedVersions.add(version);
337-
presetPrefs.put("Saved Versions", new JSONArray(savedVersions).toString());
338311
log("it worked");
339312
}
340313
}

src/main/java/com/airsquared/blobsaver/Controller.java

Lines changed: 19 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,10 @@
4040
import java.io.File;
4141
import java.io.IOException;
4242
import java.io.InputStream;
43-
import java.net.URL;
4443
import java.util.ArrayList;
4544
import java.util.Arrays;
46-
import java.util.Collections;
4745
import java.util.prefs.BackingStoreException;
4846
import java.util.prefs.Preferences;
49-
import java.util.zip.ZipEntry;
50-
import java.util.zip.ZipInputStream;
5147

5248
import static com.airsquared.blobsaver.Main.appPrefs;
5349
import static com.airsquared.blobsaver.Main.appVersion;
@@ -62,19 +58,19 @@ public class Controller {
6258
@FXML private ChoiceBox deviceTypeChoiceBox;
6359
@FXML private ChoiceBox deviceModelChoiceBox;
6460

65-
@FXML private TextField ecidField;
66-
@FXML private TextField boardConfigField;
67-
@FXML private TextField apnonceField;
68-
@FXML private TextField versionField;
69-
@FXML private TextField identifierField;
70-
@FXML private TextField pathField;
71-
@FXML private TextField ipswField;
72-
@FXML private TextField buildIDField;
61+
@FXML TextField ecidField;
62+
@FXML TextField boardConfigField;
63+
@FXML TextField apnonceField;
64+
@FXML TextField versionField;
65+
@FXML TextField identifierField;
66+
@FXML TextField pathField;
67+
@FXML TextField ipswField;
68+
@FXML TextField buildIDField;
7369

74-
@FXML private CheckBox apnonceCheckBox;
75-
@FXML private CheckBox versionCheckBox;
76-
@FXML private CheckBox identifierCheckBox;
77-
@FXML private CheckBox betaCheckBox;
70+
@FXML CheckBox apnonceCheckBox;
71+
@FXML CheckBox versionCheckBox;
72+
@FXML CheckBox identifierCheckBox;
73+
@FXML CheckBox betaCheckBox;
7874

7975
@FXML private Label versionLabel;
8076

@@ -100,13 +96,15 @@ public class Controller {
10096

10197
@FXML private Button goButton;
10298

103-
private boolean getBoardConfig = false;
99+
boolean getBoardConfig = false;
104100
private boolean editingPresets = false;
105101
private boolean choosingRunInBackground = false;
106102

107-
private static DropShadow errorBorder = new DropShadow();
103+
static DropShadow errorBorder = new DropShadow();
108104
private static DropShadow borderGlow = new DropShadow();
109105

106+
static Controller INSTANCE;
107+
110108
static void afterStageShowing() {
111109
for (int i = 1; i < 11; i++) { // sets the names for the presets
112110
if (!"".equals(appPrefs.get("Name Preset" + i, ""))) {
@@ -120,6 +118,7 @@ static void afterStageShowing() {
120118
@SuppressWarnings("unchecked")
121119
@FXML
122120
public void initialize() {
121+
INSTANCE = this;
123122
// create effects
124123
borderGlow.setOffsetY(0f);
125124
borderGlow.setOffsetX(0f);
@@ -227,147 +226,6 @@ private void requireBoardConfig(String newValue) {
227226

228227
public void checkForUpdatesHandler() { checkForUpdates(true); }
229228

230-
private void run(String device) {
231-
if ("".equals(device)) {
232-
return;
233-
}
234-
File tsschecker;
235-
File buildManifestPlist = null;
236-
try {
237-
tsschecker = getTsschecker();
238-
} catch (IOException e) {
239-
newReportableError("There was an error creating tsschecker.", e.getMessage());
240-
return;
241-
}
242-
243-
File locationToSaveBlobs = new File(pathField.getText());
244-
//noinspection ResultOfMethodCallIgnored
245-
locationToSaveBlobs.mkdirs();
246-
ArrayList<String> args = new ArrayList<>(Arrays.asList(tsschecker.getPath(), "--generator", "0x1111111111111111", "--nocache", "-d", device, "-s", "-e", ecidField.getText(), "--save-path", pathField.getText()));
247-
if (getBoardConfig) {
248-
Collections.addAll(args, "--boardconfig", boardConfigField.getText());
249-
}
250-
if (apnonceCheckBox.isSelected()) {
251-
Collections.addAll(args, "--apnonce", apnonceField.getText());
252-
}
253-
if (versionCheckBox.isSelected()) {
254-
args.add("-l");
255-
} else if (betaCheckBox.isSelected()) {
256-
try {
257-
if (!ipswField.getText().matches("https?://.*apple.*\\.ipsw")) {
258-
newUnreportableError("\"" + ipswField.getText() + "\" is not a valid URL.\n\nMake sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"");
259-
return;
260-
}
261-
buildManifestPlist = File.createTempFile("BuildManifest", ".plist");
262-
ZipInputStream zin;
263-
try {
264-
URL url = new URL(ipswField.getText());
265-
zin = new ZipInputStream(url.openStream());
266-
} catch (IOException e) {
267-
newUnreportableError("\"" + ipswField.getText() + "\" is not a valid URL.\n\nMake sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"");
268-
deleteTempFiles(buildManifestPlist);
269-
return;
270-
}
271-
ZipEntry ze;
272-
while ((ze = zin.getNextEntry()) != null) {
273-
if ("BuildManifest.plist".equals(ze.getName())) {
274-
copyStreamToFile(zin, buildManifestPlist);
275-
break;
276-
}
277-
}
278-
buildManifestPlist.deleteOnExit();
279-
} catch (IOException e) {
280-
newReportableError("Unable to get BuildManifest from .ipsw.", e.getMessage());
281-
e.printStackTrace();
282-
deleteTempFiles(buildManifestPlist);
283-
return;
284-
}
285-
Collections.addAll(args, "-i", versionField.getText(), "--beta", "--buildid", buildIDField.getText(), "-m", buildManifestPlist.toString());
286-
} else {
287-
Collections.addAll(args, "-i", versionField.getText());
288-
}
289-
String tsscheckerLog;
290-
try {
291-
log("Running: " + args.toString());
292-
tsscheckerLog = executeProgram(args.toArray(new String[0]));
293-
} catch (IOException e) {
294-
newReportableError("There was an error starting tsschecker.", e.toString());
295-
e.printStackTrace();
296-
deleteTempFiles(buildManifestPlist);
297-
return;
298-
}
299-
300-
if (tsscheckerLog.contains("Saved shsh blobs")) {
301-
Alert alert = new Alert(Alert.AlertType.INFORMATION, "Successfully saved blobs in\n" + pathField.getText(), ButtonType.OK);
302-
alert.setHeaderText("Success!");
303-
alert.showAndWait();
304-
} else if (tsscheckerLog.contains("[Error] [TSSC] manually specified ecid=" + ecidField.getText() + ", but parsing failed")) {
305-
newUnreportableError("\"" + ecidField.getText() + "\"" + " is not a valid ECID. Try getting it from iTunes.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.");
306-
ecidField.setEffect(errorBorder);
307-
} else if (tsscheckerLog.contains("[Error] [TSSC] device " + device + " could not be found in devicelist")) {
308-
Alert alert = new Alert(Alert.AlertType.ERROR, "tsschecker could not find device: \"" + device +
309-
"\"\n\nPlease create a new Github issue or PM me on Reddit if you used the dropdown menu.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.", githubIssue, redditPM, ButtonType.CANCEL);
310-
resizeAlertButtons(alert);
311-
alert.showAndWait();
312-
reportError(alert);
313-
} else if (tsscheckerLog.contains("[Error] [TSSC] ERROR: could not get url for device " + device + " on iOS " + versionField.getText())) {
314-
newUnreportableError("Could not find device \"" + device + "\" on iOS/tvOS " + versionField.getText() +
315-
"\n\nThe version doesn't exist or isn't compatible with the device");
316-
versionField.setEffect(errorBorder);
317-
} else if (tsscheckerLog.contains("[Error] [TSSC] manually specified apnonce=" + apnonceField.getText() + ", but parsing failed")) {
318-
newUnreportableError("\"" + apnonceField.getText() + "\" is not a valid apnonce");
319-
apnonceField.setEffect(errorBorder);
320-
} else if (tsscheckerLog.contains("[WARNING] [TSSC] could not get id0 for installType=Erase. Using fallback installType=Update since user did not specify installType manually")
321-
&& tsscheckerLog.contains("[Error] [TSSR] Error: could not get id0 for installType=Update")
322-
&& tsscheckerLog.contains("[Error] [TSSR] faild to build tssrequest")
323-
&& tsscheckerLog.contains("Error] [TSSC] checking tss status failed!")) {
324-
Alert alert = new Alert(Alert.AlertType.ERROR,
325-
"Saving blobs failed. Check the board configuration or try again later.\n\nIf this doesn't work, please create a new issue on Github or PM me on Reddit. The log has been copied to your clipboard.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.",
326-
githubIssue, redditPM, ButtonType.OK);
327-
resizeAlertButtons(alert);
328-
alert.showAndWait();
329-
reportError(alert, tsscheckerLog);
330-
} else if (tsscheckerLog.contains("[Error] ERROR: TSS request failed: Could not resolve host:")) {
331-
Alert alert = new Alert(Alert.AlertType.ERROR,
332-
"Saving blobs failed. Check your internet connection.\n\nIf your internet is working and you can connect to apple.com in your browser, please create a new issue on Github or PM me on Reddit. The log has been copied to your clipboard.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.",
333-
githubIssue, redditPM, ButtonType.OK);
334-
resizeAlertButtons(alert);
335-
alert.showAndWait();
336-
reportError(alert, tsscheckerLog);
337-
} else if (tsscheckerLog.contains("[Error] [Error] can't save shsh at " + pathField.getText())) {
338-
newUnreportableError("\'" + pathField.getText() + "\' is not a valid path\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.");
339-
pathField.setEffect(errorBorder);
340-
} else if (tsscheckerLog.contains("iOS " + versionField.getText() + " for device " + device + " IS NOT being signed!") || tsscheckerLog.contains("Build " + buildIDField.getText() + " for device iPhone9,2 IS NOT being signed!")) {
341-
newUnreportableError("iOS/tvOS " + versionField.getText() + " is not being signed for device " + device);
342-
versionField.setEffect(errorBorder);
343-
if (betaCheckBox.isSelected()) {
344-
buildIDField.setEffect(errorBorder);
345-
ipswField.setEffect(errorBorder);
346-
}
347-
} else if (tsscheckerLog.contains("[Error] [TSSC] failed to load manifest")) {
348-
Alert alert = new Alert(Alert.AlertType.ERROR,
349-
"Failed to load manifest.\n\n \"" + ipswField.getText() + "\" might not be a valid URL.\n\nMake sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"\n\nIf the URL is fine, please create a new issue on Github or PM me on Reddit. The log has been copied to your clipboard",
350-
githubIssue, redditPM, ButtonType.OK);
351-
resizeAlertButtons(alert);
352-
alert.showAndWait();
353-
reportError(alert, tsscheckerLog);
354-
} else if (tsscheckerLog.contains("[Error] [TSSC] selected device can't be used with that buildmanifest")) {
355-
newUnreportableError("Device and build manifest don't match.");
356-
} else if (tsscheckerLog.contains("[Error]")) {
357-
newReportableError("Saving blobs failed.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.", tsscheckerLog);
358-
} else {
359-
newReportableError("Unknown result.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.", tsscheckerLog);
360-
}
361-
deleteTempFiles(buildManifestPlist);
362-
}
363-
364-
@SuppressWarnings("ResultOfMethodCallIgnored")
365-
private static void deleteTempFiles(File buildManifestPlist) {
366-
if (buildManifestPlist != null && buildManifestPlist.exists()) {
367-
buildManifestPlist.delete();
368-
}
369-
}
370-
371229
public void apnonceCheckBoxHandler() {
372230
if (apnonceCheckBox.isSelected()) {
373231
apnonceField.setDisable(false);
@@ -1083,7 +941,7 @@ public void goButtonHandler() {
1083941
String identifierText = identifierField.getText();
1084942
try {
1085943
if (identifierText.startsWith("iPad") || identifierText.startsWith("iPod") || identifierText.startsWith("iPhone") || identifierText.startsWith("AppleTV")) {
1086-
run(identifierField.getText());
944+
TSSChecker.run(identifierField.getText());
1087945
} else {
1088946
identifierField.setEffect(errorBorder);
1089947
newUnreportableError("\"" + identifierText + "\" is not a valid identifier");
@@ -1092,7 +950,7 @@ public void goButtonHandler() {
1092950
newUnreportableError("\"" + identifierText + "\" is not a valid identifier");
1093951
}
1094952
} else {
1095-
run(textToIdentifier(deviceModel));
953+
TSSChecker.run(textToIdentifier(deviceModel));
1096954
}
1097955
}
1098956

src/main/java/com/airsquared/blobsaver/Shared.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import javafx.scene.control.Button;
2727
import javafx.scene.control.ButtonBar;
2828
import javafx.scene.control.ButtonType;
29+
import org.json.JSONArray;
2930
import org.json.JSONException;
3031
import org.json.JSONObject;
3132

@@ -46,7 +47,10 @@
4647
import java.nio.file.SimpleFileVisitor;
4748
import java.nio.file.attribute.BasicFileAttributes;
4849
import java.util.Collections;
50+
import java.util.List;
51+
import java.util.Map;
4952
import java.util.concurrent.CountDownLatch;
53+
import java.util.stream.Collectors;
5054
import java.util.stream.Stream;
5155

5256
import static com.airsquared.blobsaver.Main.appPrefs;
@@ -395,4 +399,11 @@ static void runSafe(Runnable runnable) {
395399
Platform.runLater(runnable);
396400
}
397401
}
402+
403+
static List<String> getAllSignedVersions(String deviceIdentifier) throws IOException {
404+
String response = makeRequest(new URL("https://api.ipsw.me/v4/device/" + deviceIdentifier));
405+
JSONArray firmwareListJson = new JSONObject(response).getJSONArray("firmwares");
406+
@SuppressWarnings("unchecked") List<Map<String, Object>> firmwareList = (List) firmwareListJson.toList();
407+
return firmwareList.stream().filter(map -> Boolean.TRUE.equals(map.get("signed"))).map(map -> map.get("version").toString()).collect(Collectors.toList());
408+
}
398409
}

0 commit comments

Comments
 (0)