@@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
|
|||||||
import com.jfoenix.controls.JFXDialogLayout;
|
import com.jfoenix.controls.JFXDialogLayout;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
@@ -39,7 +38,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.loadFXML;
|
import static org.jackhuang.hmcl.ui.FXUtils.loadFXML;
|
||||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public class AddAuthlibInjectorServerPane extends StackPane implements DialogAware {
|
public class AddAuthlibInjectorServerPane extends StackPane implements DialogAware {
|
||||||
@@ -70,24 +68,13 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
|
|||||||
transitionHandler = new TransitionHandler(addServerContainer);
|
transitionHandler = new TransitionHandler(addServerContainer);
|
||||||
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||||
|
|
||||||
btnAddNext.disableProperty().bind(
|
btnAddNext.disableProperty().bind(txtServerUrl.textProperty().isEmpty());
|
||||||
Bindings.createBooleanBinding(txtServerUrl::validate, txtServerUrl.textProperty()).not());
|
|
||||||
nextPane.hideSpinner();
|
nextPane.hideSpinner();
|
||||||
|
|
||||||
txtServerUrl.setText("https://");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDialogShown() {
|
public void onDialogShown() {
|
||||||
txtServerUrl.requestFocus();
|
txtServerUrl.requestFocus();
|
||||||
txtServerUrl.selectEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String fixInputUrl(String url) {
|
|
||||||
if (!url.endsWith("/")) {
|
|
||||||
url += "/";
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveFetchExceptionMessage(Throwable exception) {
|
private String resolveFetchExceptionMessage(Throwable exception) {
|
||||||
@@ -110,13 +97,13 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
|
|||||||
|
|
||||||
lblCreationWarning.setText("");
|
lblCreationWarning.setText("");
|
||||||
|
|
||||||
String url = fixInputUrl(txtServerUrl.getText());
|
String url = txtServerUrl.getText();
|
||||||
|
|
||||||
nextPane.showSpinner();
|
nextPane.showSpinner();
|
||||||
addServerPane.setDisable(true);
|
addServerPane.setDisable(true);
|
||||||
|
|
||||||
Task.of(() -> {
|
Task.of(() -> {
|
||||||
serverBeingAdded = AuthlibInjectorServer.fetchServerInfo(url);
|
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
|
||||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||||
addServerPane.setDisable(false);
|
addServerPane.setDisable(false);
|
||||||
nextPane.hideSpinner();
|
nextPane.hideSpinner();
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package org.jackhuang.hmcl.ui.construct;
|
|
||||||
|
|
||||||
import com.jfoenix.validation.base.ValidatorBase;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.scene.control.TextInputControl;
|
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
public class URLValidator extends ValidatorBase {
|
|
||||||
|
|
||||||
private final ObservableList<String> protocols = FXCollections.observableArrayList();
|
|
||||||
|
|
||||||
public URLValidator() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableList<String> getProtocols() {
|
|
||||||
return protocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void eval() {
|
|
||||||
if (srcControl.get() instanceof TextInputControl) {
|
|
||||||
try {
|
|
||||||
URL url = new URL(((TextInputControl) srcControl.get()).getText());
|
|
||||||
if (protocols.isEmpty())
|
|
||||||
hasErrors.set(false);
|
|
||||||
else
|
|
||||||
hasErrors.set(!protocols.contains(url.getProtocol()));
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
hasErrors.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.*?>
|
||||||
<?import org.jackhuang.hmcl.ui.construct.URLValidator?>
|
|
||||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||||
|
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
@@ -16,16 +15,7 @@
|
|||||||
<Label text="%account.injector.add" />
|
<Label text="%account.injector.add" />
|
||||||
</heading>
|
</heading>
|
||||||
<body>
|
<body>
|
||||||
<JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url" onAction="#onAddNext">
|
<JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url" onAction="#onAddNext" />
|
||||||
<validators>
|
|
||||||
<URLValidator message="%input.url">
|
|
||||||
<protocols>
|
|
||||||
<String fx:value="http" />
|
|
||||||
<String fx:value="https" />
|
|
||||||
</protocols>
|
|
||||||
</URLValidator>
|
|
||||||
</validators>
|
|
||||||
</JFXTextField>
|
|
||||||
</body>
|
</body>
|
||||||
<actions>
|
<actions>
|
||||||
<Label fx:id="lblCreationWarning" />
|
<Label fx:id="lblCreationWarning" />
|
||||||
|
|||||||
@@ -17,23 +17,104 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.authlibinjector;
|
package org.jackhuang.hmcl.auth.authlibinjector;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
|
import static org.jackhuang.hmcl.util.io.IOUtils.readFullyWithoutClosing;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
|
||||||
|
|
||||||
public class AuthlibInjectorServer {
|
public class AuthlibInjectorServer {
|
||||||
|
|
||||||
public static AuthlibInjectorServer fetchServerInfo(String url) throws IOException {
|
private static final int MAX_REDIRECTS = 5;
|
||||||
|
|
||||||
|
public static AuthlibInjectorServer locateServer(String url) throws IOException {
|
||||||
|
url = parseInputUrl(url);
|
||||||
|
HttpURLConnection conn;
|
||||||
|
int redirectCount = 0;
|
||||||
|
for (;;) {
|
||||||
|
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
Optional<String> ali = getApiLocationIndication(conn);
|
||||||
|
if (ali.isPresent()) {
|
||||||
|
conn.disconnect();
|
||||||
|
url = ali.get();
|
||||||
|
if (redirectCount >= MAX_REDIRECTS) {
|
||||||
|
throw new IOException("Exceeded maximum number of redirects (" + MAX_REDIRECTS + ")");
|
||||||
|
}
|
||||||
|
redirectCount++;
|
||||||
|
LOG.info("Redirect API root to: " + url);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(NetworkUtils.toURL(url)), JsonObject.class);
|
return parseResponse(url, readFullyWithoutClosing(conn.getInputStream()));
|
||||||
|
} finally {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<String> getApiLocationIndication(URLConnection conn) {
|
||||||
|
return Optional.ofNullable(conn.getHeaderFields().get("X-Authlib-Injector-API-Location"))
|
||||||
|
.flatMap(list -> list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)))
|
||||||
|
.flatMap(indication -> {
|
||||||
|
String currentUrl = appendSuffixSlash(conn.getURL().toString());
|
||||||
|
String newUrl;
|
||||||
|
try {
|
||||||
|
newUrl = appendSuffixSlash(new URL(conn.getURL(), indication).toString());
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
LOG.warning("Failed to resolve absolute ALI, the header is [" + indication + "]. Ignore it.");
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newUrl.equals(currentUrl)) {
|
||||||
|
return Optional.empty();
|
||||||
|
} else {
|
||||||
|
return Optional.of(newUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseInputUrl(String url) {
|
||||||
|
String lowercased = url.toLowerCase();
|
||||||
|
if (!lowercased.startsWith("http://") && !lowercased.startsWith("https://")) {
|
||||||
|
url = "https://" + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = appendSuffixSlash(url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String appendSuffixSlash(String url) {
|
||||||
|
if (!url.endsWith("/")) {
|
||||||
|
return url + "/";
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthlibInjectorServer fetchServerInfo(String url) throws IOException {
|
||||||
|
try (InputStream in = new URL(url).openStream()) {
|
||||||
|
return parseResponse(url, readFullyWithoutClosing(in));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AuthlibInjectorServer parseResponse(String url, byte[] rawResponse) throws IOException {
|
||||||
|
try {
|
||||||
|
JsonObject response = JsonUtils.fromNonNullJson(new String(rawResponse, UTF_8), JsonObject.class);
|
||||||
String name = extractServerName(response).orElse(url);
|
String name = extractServerName(response).orElse(url);
|
||||||
return new AuthlibInjectorServer(url, name);
|
return new AuthlibInjectorServer(url, name);
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user