Skip to content

Commit 6d2646b

Browse files
committed
Allow specifying a local IPSW or BuildManifest.plist file URL
1 parent d099187 commit 6d2646b

File tree

5 files changed

+61
-40
lines changed

5 files changed

+61
-40
lines changed

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

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import javafx.scene.input.KeyCode;
3737
import javafx.scene.input.KeyEvent;
3838
import javafx.scene.layout.HBox;
39+
import javafx.scene.layout.Pane;
3940
import javafx.scene.layout.VBox;
4041
import javafx.stage.FileChooser;
4142
import javafx.stage.Modality;
@@ -66,7 +67,7 @@ public class Controller {
6667
@FXML private ToggleButton backgroundSettingsButton;
6768

6869
@FXML private ListView<Prefs.SavedDevice> deviceList;
69-
@FXML private VBox savedDevicesVBox;
70+
@FXML private Pane savedDevicesVBox;
7071

7172
public static final String initialPath = System.getProperty("user.home") + File.separator + "Blobs";
7273

@@ -174,7 +175,17 @@ public void manualURLCheckBoxHandler() {
174175
}
175176
}
176177

177-
public void filePickerHandler() {
178+
public void ipswPickerHandler() {
179+
FileChooser chooser = new FileChooser();
180+
chooser.setTitle("Choose an IPSW or Build Manifest file");
181+
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("IPSW files, Plist files", "*.ipsw", "*.plist"));
182+
File file = chooser.showOpenDialog(Main.primaryStage);
183+
if (file != null) {
184+
ipswField.setText(file.toURI().toString());
185+
}
186+
}
187+
188+
public void folderPickerHandler() {
178189
File result = Utils.showFilePickerDialog(Main.primaryStage, new File(pathField.getText()));
179190
if (result != null) {
180191
pathField.setText(result.getAbsolutePath());
@@ -301,35 +312,29 @@ public void checkBlobs() {
301312
Analytics.checkBlobs();
302313
}
303314

304-
public void helpLabelHandler(Event evt) {
315+
public void locationHelp() {
316+
ButtonType openURL = new ButtonType("Open URL");
317+
Alert alert = new Alert(Alert.AlertType.INFORMATION,
318+
"Click \"Open URL\" to see how to automatically upload blobs you save to the cloud.", openURL, ButtonType.OK);
319+
alert.setTitle("Tip: Saving Blobs to the Cloud");
320+
alert.setHeaderText("Tip");
321+
alert.showAndWait();
322+
if (openURL.equals(alert.getResult())) {
323+
Utils.openURL("https://github.com/airsquared/blobsaver/wiki/Automatically-saving-blobs-to-the-cloud");
324+
}
325+
}
326+
327+
public void ipswURLHelp() {
305328
if (Main.SHOW_BREAKPOINT) {
306329
return; // remember to put a breakpoint here
307330
}
308331

309-
String labelID = ((Label) evt.getSource()).getId();
310-
311-
ButtonType openURL = new ButtonType("Open URL");
312-
ButtonType customOK = new ButtonType("OK", ButtonBar.ButtonData.CANCEL_CLOSE);
313-
Alert alert = new Alert(Alert.AlertType.INFORMATION, "", openURL, customOK);
314-
((Button) alert.getDialogPane().lookupButton(customOK)).setDefaultButton(true);
315-
String url = switch (labelID) {
316-
case "ipswURLHelp":
317-
alert.setContentText("Get the IPSW download URL for the iOS version from theiphonewiki.com/wiki/Beta_Firmware and paste it here.\n\nIf you already have the IPSW downloaded, you can also supply a 'file:' URL.");
318-
alert.setTitle("Help: IPSW URL");
319-
alert.setHeaderText("Help");
320-
yield "https://www.theiphonewiki.com/wiki/Beta_Firmware";
321-
case "locationHelp":
322-
alert.setContentText("Click \"Open URL\" to see how to automatically upload blobs you save to the cloud.");
323-
alert.setTitle("Tip: Saving Blobs to the Cloud");
324-
alert.setHeaderText("Tip");
325-
yield "https://github.com/airsquared/blobsaver/wiki/Automatically-saving-blobs-to-the-cloud";
326-
default:
327-
throw new IllegalStateException("Unexpected value for labelID: " + labelID);
328-
};
332+
Alert alert = new Alert(Alert.AlertType.INFORMATION,
333+
"You can supply either a URL to an IPSW file or a build manifest (.plist). " +
334+
"If you already have the file downloaded, you can also use a 'file:' URL to either an IPSW or a build manifest plist.");
335+
alert.setTitle("Help: IPSW URL");
336+
alert.setHeaderText("Help");
329337
alert.showAndWait();
330-
if (openURL.equals(alert.getResult())) {
331-
Utils.openURL(url);
332-
}
333338
}
334339

335340
public void aboutMenuHandler(Event ignored) {
@@ -551,7 +556,7 @@ public void readInfo() {
551556
boardConfigField.setText(LibimobiledeviceUtil.getBoardConfig());
552557
}
553558
} catch (LibimobiledeviceUtil.LibimobiledeviceException e) {
554-
System.err.println(e.getMessage()); // don't need full stack trace
559+
System.err.println(e.getMessage()); // don't need a full stack trace
555560
e.showErrorAlert();
556561
} catch (Throwable e) {
557562
e.printStackTrace();

src/main/java/airsquared/blobsaver/app/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ static void setJNALibraryPath() {
8787
}
8888

8989
/**
90-
* Apple Decided use some old distrusted certificate for the iOS 15.5 IPSW, causing this error:
90+
* Apple decided use some old distrusted certificate for the iOS 15.5 IPSW, causing this error:
9191
* {@code javax.net.ssl.SSLHandshakeException: TLS Server certificate issued after 2019-12-31 and anchored by a distrusted legacy Symantec root CA: CN=GeoTrust Primary Certification Authority - G2, OU=(c) 2007 GeoTrust Inc. - For authorized use only, O=GeoTrust Inc., C=US }
9292
*
9393
* This uses reflection/unsafe to make the certificate "trusted" anyway.

src/main/java/airsquared/blobsaver/app/TSS.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848

4949
public class TSS extends Task<String> {
5050

51-
private static final Pattern ipswURLPattern = Pattern.compile("(https?://|file:/).*\\.ipsw");
51+
private static final Pattern ipswURLPattern = Pattern.compile("(https?://|file:/).*\\.(ipsw|plist)");
5252
private static final Pattern versionPattern = Pattern.compile("[0-9]+\\.[0-9]+\\.?[0-9]*(?<!\\.)");
5353

5454
private final String deviceIdentifier;
@@ -165,12 +165,12 @@ private void checkInputs() throws TSSException {
165165
}
166166
new URL(manualIpswURL); // check URL
167167
} catch (MalformedURLException e) {
168-
throw new TSSException("The IPSW URL is not valid.\n\nMake sure it's a valid URL that ends with \".ipsw\"", false, e);
168+
throw new TSSException("The IPSW URL is not valid.\n\nMake sure it's a valid URL that ends with \".ipsw\" or \".plist\"", false, e);
169169
}
170170
if (manualIpswURL.startsWith("file:")) try {
171171
Path.of(new URI(manualIpswURL)).toRealPath(); // check URI
172172
} catch (IllegalArgumentException | URISyntaxException | IOException e) {
173-
throw new TSSException("The IPSW URL is not valid.\n\nMake sure it's a valid file URL to a local .ipsw file.", false, e);
173+
throw new TSSException("The IPSW URL is not valid.\n\nMake sure it's a valid file URL to a local .ipsw or .plist file.", false, e);
174174
}
175175
} else if (manualVersion != null && !versionPattern.matcher(manualVersion).matches()) {
176176
throw new TSSException("Invalid version. Make sure it follows the convention X.X.X or X.X, like \"13.1\" or \"13.5.5\"", false);
@@ -259,8 +259,7 @@ && containsIgnoreCase(tsscheckerLog, "checking tss status failed")) {
259259

260260
} else if (containsIgnoreCase(tsscheckerLog, "failed to load manifest")) {
261261
if (manualIpswURL != null) {
262-
throw new TSSException("Failed to load manifest. The IPSW URL is not valid.\n\n" +
263-
"Make sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"", false);
262+
throw new TSSException("Failed to load manifest. The IPSW or build manifest URL is not valid.\n\n", false);
264263
} else {
265264
throw new TSSException("Failed to load manifest.", true, tsscheckerLog);
266265
}

src/main/java/airsquared/blobsaver/app/Utils.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private static void _checkForUpdates(boolean forceCheck) {
110110
try {
111111
newVersion = LatestVersion.request();
112112
} catch (IOException e) {
113-
runLater(() -> showReportableError("Unable to check for updates.", e.toString()));
113+
runLater(() -> showReportableError("Unable to check for updates.", exceptionToString(e)));
114114
throw new UncheckedIOException(e);
115115
}
116116

@@ -385,6 +385,16 @@ static Path extractBuildManifest(String ipswUrl) throws IOException {
385385
} catch (Exception e) {
386386
e.printStackTrace();
387387
}
388+
} else if (ipswUrl.startsWith("file:") && ipswUrl.endsWith(".plist")) {
389+
Files.copy(Path.of(URI.create(ipswUrl)), buildManifest, StandardCopyOption.REPLACE_EXISTING);
390+
return buildManifest.toRealPath();
391+
} else if (ipswUrl.endsWith(".plist")) {
392+
var manifestURL = new URL(ipswUrl);
393+
try (var stream = manifestURL.openStream()) {
394+
Files.copy(stream, buildManifest, StandardCopyOption.REPLACE_EXISTING);
395+
System.out.println("Directly downloaded to " + buildManifest);
396+
return buildManifest.toRealPath();
397+
}
388398
}
389399
extractManifestFromZip(ipswUrl, buildManifest);
390400
System.out.println("Extracted to " + buildManifest);

src/main/resources/airsquared/blobsaver/app/blobsaver.fxml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,20 @@
185185
</VBox.margin>
186186
<TextField fx:id="versionField" disable="${allSignedVersionsCheckBox.selected || manualURLCheckBox.selected}"
187187
promptText="Version" onTextChange="#clearEffectHandler"/>
188-
<TextField fx:id="ipswField" disable="${!manualURLCheckBox.selected}" HBox.hgrow="ALWAYS"
189-
promptText="URL to .ipsw file" onTextChange="#clearEffectHandler">
188+
<AnchorPane disable="${!manualURLCheckBox.selected}" HBox.hgrow="ALWAYS">
190189
<HBox.margin>
191190
<Insets left="5.0"/>
192191
</HBox.margin>
193-
</TextField>
194-
<Label id="ipswURLHelp" onMouseClicked="#helpLabelHandler" text="?">
192+
<TextField fx:id="ipswField" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
193+
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
194+
promptText="URL to .ipsw or build manifest .plist" onTextChange="#clearEffectHandler">
195+
<padding>
196+
<Insets left="8.0" right="33.0"/>
197+
</padding>
198+
</TextField>
199+
<Button mnemonicParsing="false" onAction="#ipswPickerHandler" text="..." AnchorPane.rightAnchor="0.0"/>
200+
</AnchorPane>
201+
<Label id="ipswURLHelp" onMouseClicked="#ipswURLHelp" text="?">
195202
<padding>
196203
<Insets bottom="5.0" left="5.0"/>
197204
</padding>
@@ -255,9 +262,9 @@
255262
</padding>
256263
<Controller fx:constant="initialPath"/>
257264
</TextField>
258-
<Button mnemonicParsing="false" onAction="#filePickerHandler" text="..." AnchorPane.rightAnchor="0.0"/>
265+
<Button mnemonicParsing="false" onAction="#folderPickerHandler" text="..." AnchorPane.rightAnchor="0.0"/>
259266
</AnchorPane>
260-
<Label id="locationHelp" onMouseClicked="#helpLabelHandler" text="?">
267+
<Label id="locationHelp" onMouseClicked="#locationHelp" text="?">
261268
<padding>
262269
<Insets bottom="5.0" left="5.0"/>
263270
</padding>

0 commit comments

Comments
 (0)