fix: version id checking not working properly

This commit is contained in:
Haowei Wen
2021-08-20 23:13:11 +08:00
parent 2022af8a53
commit c660b1d3f8
14 changed files with 111 additions and 62 deletions

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@ import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
import java.io.IOException;
@@ -283,10 +284,6 @@ public class HMCLGameRepository extends DefaultGameRepository {
vs.setUsesGlobal(true);
}
public boolean forbidsVersion(String id) {
return FORBIDDEN.contains(id);
}
public LaunchOptions getLaunchOptions(String version, File gameDir, boolean checkJava) throws InterruptedException {
VersionSetting vs = getVersionSetting(version);
@@ -368,7 +365,38 @@ public class HMCLGameRepository extends DefaultGameRepository {
.setPrettyPrinting()
.create();
private static final HashSet<String> FORBIDDEN = new HashSet<>(Arrays.asList("modpack", "minecraftinstance", "manifest"));
private static final String PROFILE = "{\"selectedProfile\": \"(Default)\",\"profiles\": {\"(Default)\": {\"name\": \"(Default)\"}},\"clientToken\": \"88888888-8888-8888-8888-888888888888\"}";
// These version ids are forbidden because they may conflict with modpack configuration filenames
private static final Set<String> FORBIDDEN_VERSION_IDS = new HashSet<>(Arrays.asList(
"modpack", "minecraftinstance", "manifest"));
public static boolean isValidVersionId(String id) {
if (FORBIDDEN_VERSION_IDS.contains(id))
return false;
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS &&
FORBIDDEN_VERSION_IDS.contains(id.toLowerCase()))
return false;
return OperatingSystem.isNameValid(id);
}
/**
* Returns true if the given version id conflicts with an existing version.
*/
public boolean versionIdConflicts(String id) {
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
// on Windows, filenames are case-insensitive
for (String existingId : versions.keySet()) {
if (existingId.equalsIgnoreCase(id)) {
return true;
}
}
return false;
} else {
return versions.containsKey(id);
}
}
}

View File

@@ -1,13 +1,34 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.validation.base.ValidatorBase;
import javafx.beans.NamedArg;
import javafx.scene.control.TextInputControl;
import org.jackhuang.hmcl.util.StringUtils;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class RequiredValidator extends ValidatorBase {
public RequiredValidator() {
this(i18n("input.not_empty"));
}
public RequiredValidator(@NamedArg("message") String message) {

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@ import javafx.beans.property.SimpleBooleanProperty;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.ui.InstallerItem;
import org.jackhuang.hmcl.ui.wizard.WizardController;
@@ -40,7 +41,7 @@ class AdditionalInstallersPage extends InstallersPage {
protected final String gameVersion;
protected final Version version;
public AdditionalInstallersPage(String gameVersion, Version version, WizardController controller, GameRepository repository, InstallerWizardDownloadProvider downloadProvider) {
public AdditionalInstallersPage(String gameVersion, Version version, WizardController controller, HMCLGameRepository repository, InstallerWizardDownloadProvider downloadProvider) {
super(controller, repository, gameVersion, downloadProvider);
this.gameVersion = gameVersion;
this.version = version;

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
@@ -32,16 +31,16 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.ui.InstallerItem;
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.util.Map;
import static javafx.beans.binding.Bindings.createBooleanBinding;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class InstallersPage extends Control implements WizardPage {
@@ -51,16 +50,14 @@ public class InstallersPage extends Control implements WizardPage {
protected JFXTextField txtName = new JFXTextField();
protected BooleanProperty installable = new SimpleBooleanProperty();
public InstallersPage(WizardController controller, GameRepository repository, String gameVersion, InstallerWizardDownloadProvider downloadProvider) {
public InstallersPage(WizardController controller, HMCLGameRepository repository, String gameVersion, InstallerWizardDownloadProvider downloadProvider) {
this.controller = controller;
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
hasVersion.setMessage(i18n("install.new_game.already_exists"));
Validator nameValidator = new Validator(OperatingSystem::isNameValid);
nameValidator.setMessage(i18n("install.new_game.malformed"));
txtName.getValidators().addAll(hasVersion, nameValidator);
installable.bind(Bindings.createBooleanBinding(() -> txtName.validate(),
txtName.textProperty()));
txtName.getValidators().addAll(
new RequiredValidator(),
new Validator(i18n("install.new_game.already_exists"), str -> !repository.versionIdConflicts(str)),
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
installable.bind(createBooleanBinding(txtName::validate, txtName.textProperty()));
txtName.setText(gameVersion);
group.game.installable.setValue(false);

View File

@@ -20,12 +20,13 @@ package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Profile;
@@ -35,17 +36,18 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.WebStage;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.CompressingUtils;
import java.io.File;
import java.util.Map;
import java.util.Optional;
import static javafx.beans.binding.Bindings.createBooleanBinding;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -93,15 +95,14 @@ public final class LocalModpackPage extends StackPane implements WizardPage {
txtModpackName.setDisable(true);
} else {
txtModpackName.getValidators().addAll(
new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)),
new Validator(i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str))
);
new RequiredValidator(),
new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().versionIdConflicts(str)),
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
btnInstall.disableProperty().bind(
createBooleanBinding(txtModpackName::validate, txtModpackName.textProperty())
.not());
}
btnInstall.disableProperty().bind(Bindings.createBooleanBinding(
() -> !txtModpackName.validate(),
txtModpackName.textProperty()));
Optional<File> filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class);
if (filePath.isPresent()) {
selectedFile = filePath.get();

View File

@@ -24,21 +24,24 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.WebStage;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import static javafx.beans.binding.Bindings.createBooleanBinding;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -104,10 +107,12 @@ public class RemoteModpackPage extends StackPane implements WizardPage {
// trim: https://github.com/huanghongxun/HMCL/issues/962
txtModpackName.setText(manifest.getName().trim());
txtModpackName.getValidators().addAll(
new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)),
new Validator(i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str))
);
txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate()));
new RequiredValidator(),
new Validator(i18n("install.new_game.already_exists"), str -> !profile.getRepository().versionIdConflicts(str)),
new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId));
btnInstall.disableProperty().bind(
createBooleanBinding(txtModpackName::validate, txtModpackName.textProperty())
.not());
}
}

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -215,7 +215,7 @@ public final class ModpackInfoPage extends Control implements WizardPage {
txtModpackName.textProperty().bindBidirectional(skinnable.name);
txtModpackName.setLabelFloat(true);
txtModpackName.setPromptText(i18n("modpack.name"));
txtModpackName.getValidators().add(new RequiredValidator(i18n("modpack.not_a_valid_name")));
txtModpackName.getValidators().add(new RequiredValidator());
StackPane.setMargin(txtModpackName, insets);
list.getContent().add(txtModpackName);

View File

@@ -52,8 +52,7 @@
<JFXTextField fx:id="txtUsername" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.columnSpan="2"
FXUtils.validateWhileTextChanged="true" onAction="#onCreationAccept">
<validators>
<RequiredValidator message="%input.not_empty">
</RequiredValidator>
<RequiredValidator />
</validators>
</JFXTextField>
@@ -62,8 +61,7 @@
<JFXPasswordField fx:id="txtPassword" GridPane.columnIndex="1" GridPane.rowIndex="3"
GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true" onAction="#onCreationAccept">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
<RequiredFieldValidator />
</validators>
</JFXPasswordField>
</GridPane>

View File

@@ -525,7 +525,6 @@ version.cannot_read=Unable to find the game version. Cannot continue automatic i
version.empty=No game
version.empty.add=Install new version
version.empty.launch=No version to launch, please install a version via version list page.
version.forbidden_name=Forbidden name please do not use that name.
version.game.old=Old
version.game.release=Releases
version.game.snapshot=Snapshots

View File

@@ -420,7 +420,6 @@ version.cannot_read=No se pudo encontrar la versión de juego. Descontinuando au
version.empty=No hay juego
version.empty.add=Instalar juego en lista de juegos
version.empty.launch=No hay juego para iniciar, por favor instale juego nuevo via la lista.
version.forbidden_name=Nombre prohibido.
version.game.old=Viejo
version.game.release=Release
version.game.snapshot=Snapshot

View File

@@ -437,7 +437,6 @@ version.cannot_read=Невозможно найти версию игры. Не
version.empty=Нет установленных
version.empty.add=Установить новую версию
version.empty.launch=Нет версии для запуска, установите версию на странице списка версий.
version.forbidden_name=Запрещённое название, не используйте его.
version.game.old=Старые
version.game.release=Релизы
version.game.snapshot=Snapshots

View File

@@ -443,7 +443,6 @@ version.cannot_read=讀取遊戲版本失敗,無法進行自動安裝
version.empty=無遊戲版本
version.empty.add=進入遊戲列表安裝
version.empty.launch=沒有可啟動的遊戲,你可以點選左側遊戲欄內的設定按鈕進入遊戲列表安裝遊戲
version.forbidden_name=此版本名稱不受支援,請換一個名稱
version.game.old=老舊版本
version.game.release=穩定版本
version.game.snapshot=測試版本

View File

@@ -529,7 +529,6 @@ version.cannot_read=读取游戏版本失败,无法进行自动安装
version.empty=没有游戏版本
version.empty.add=进入版本列表安装
version.empty.launch=没有可启动的游戏,你可以点击左侧游戏栏内的设置按钮进入版本列表安装游戏
version.forbidden_name=此版本名称不受支持,请换一个名字
version.game.old=远古版
version.game.release=正式版
version.game.snapshot=测试版

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -118,12 +118,12 @@ public enum OperatingSystem {
if (CURRENT_OS == WINDOWS) {
// valid names and characters taken from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
INVALID_RESOURCE_CHARACTERS = Pattern.compile("[/\"<>|?*:\\\\]");
INVALID_RESOURCE_BASENAMES = new String[]{"aux", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
"com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
INVALID_RESOURCE_BASENAMES = new String[]{"aux", "com1", "com2", "com3", "com4",
"com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2",
"lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn"};
Arrays.sort(INVALID_RESOURCE_BASENAMES);
//CLOCK$ may be used if an extension is provided
INVALID_RESOURCE_FULLNAMES = new String[]{"clock$"}; //$NON-NLS-1$
INVALID_RESOURCE_FULLNAMES = new String[]{"clock$"};
} else {
//only front slash and null char are invalid on UNIXes
//taken from http://www.faqs.org/faqs/unix-faq/faq/part2/section-2.html
@@ -168,19 +168,22 @@ public enum OperatingSystem {
}
/**
* Returns true if the given name is a valid resource name on this operating system,
* Returns true if the given name is a valid file name on this operating system,
* and false otherwise.
*/
public static boolean isNameValid(String name) {
//. and .. have special meaning on all platforms
if (name.equals(".") || name.equals("..") || name.indexOf('/') == 0 || name.indexOf('\0') >= 0) //$NON-NLS-1$ //$NON-NLS-2$
// empty filename is not allowed
if (name.isEmpty())
return false;
if (CURRENT_OS == WINDOWS) {
//empty names are not valid
final int length = name.length();
if (length == 0)
return false;
final char lastChar = name.charAt(length - 1);
// . and .. have special meaning on all platforms
if (name.isEmpty() || name.equals("."))
return false;
// \0 and / are forbidden on all platforms
if (name.indexOf('/') != -1 || name.indexOf('\0') != -1)
return false;
if (CURRENT_OS == WINDOWS) { // Windows only
char lastChar = name.charAt(name.length() - 1);
// filenames ending in dot are not valid
if (lastChar == '.')
return false;
@@ -194,7 +197,7 @@ public enum OperatingSystem {
return false;
if (Arrays.binarySearch(INVALID_RESOURCE_FULLNAMES, name.toLowerCase()) >= 0)
return false;
if (INVALID_RESOURCE_CHARACTERS != null && INVALID_RESOURCE_CHARACTERS.matcher(name).find())
if (INVALID_RESOURCE_CHARACTERS.matcher(name).find())
return false;
}