mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-23 09:01:08 +03:00
Merge branch 'release/5.1.4'
This commit is contained in:
commit
aed80e995a
185 changed files with 4209 additions and 4301 deletions
126
.github/workflows/push.yml
vendored
126
.github/workflows/push.yml
vendored
|
@ -1,5 +1,5 @@
|
|||
name: push
|
||||
on:
|
||||
on:
|
||||
push:
|
||||
create:
|
||||
tags:
|
||||
|
@ -9,74 +9,74 @@ jobs:
|
|||
name: Launcher
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Create artifacts
|
||||
run: |
|
||||
mkdir -p artifacts/modules
|
||||
cd LaunchServer/build/libs/
|
||||
zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar"
|
||||
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
|
||||
cd ../../../ServerWrapper/build/libs
|
||||
cp ServerWrapper.jar ../../../artifacts/ServerWrapper.jar
|
||||
cd ../../../LauncherAuthlib/build/libs
|
||||
cp LauncherAuthlib.jar ../../../artifacts/LauncherAuthlib.jar
|
||||
cd ../../../
|
||||
cp modules/*_module/build/libs/*.jar artifacts/modules
|
||||
cp modules/*_swmodule/build/libs/*.jar artifacts/modules
|
||||
cp modules/*_lmodule/build/libs/*.jar artifacts/modules
|
||||
- name: Create artifacts
|
||||
run: |
|
||||
mkdir -p artifacts/modules
|
||||
cd LaunchServer/build/libs/
|
||||
zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar"
|
||||
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
|
||||
cd ../../../ServerWrapper/build/libs
|
||||
cp ServerWrapper.jar ../../../artifacts/ServerWrapper.jar
|
||||
cd ../../../LauncherAuthlib/build/libs
|
||||
cp LauncherAuthlib.jar ../../../artifacts/LauncherAuthlib.jar
|
||||
cd ../../../
|
||||
cp modules/*_module/build/libs/*.jar artifacts/modules
|
||||
cp modules/*_swmodule/build/libs/*.jar artifacts/modules
|
||||
cp modules/*_lmodule/build/libs/*.jar artifacts/modules
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Launcher
|
||||
path: artifacts
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Launcher
|
||||
path: artifacts
|
||||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
if: github.event_name == 'create'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: GravitLauncher ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
if: github.event_name == 'create'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: GravitLauncher ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Pack release
|
||||
if: github.event_name == 'create'
|
||||
run: |
|
||||
cd artifacts/
|
||||
zip -r -9 ../Release.zip *
|
||||
- name: Pack release
|
||||
if: github.event_name == 'create'
|
||||
run: |
|
||||
cd artifacts/
|
||||
zip -r -9 ../Release.zip *
|
||||
|
||||
- name: Upload release
|
||||
if: github.event_name == 'create'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./Release.zip
|
||||
asset_name: Release.zip
|
||||
asset_content_type: application/zip
|
||||
- name: Upload release
|
||||
if: github.event_name == 'create'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./Release.zip
|
||||
asset_name: Release.zip
|
||||
asset_content_type: application/zip
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -107,6 +107,6 @@ buildnumber
|
|||
*.directory
|
||||
cmd.bat
|
||||
cmd.sh
|
||||
project/target
|
||||
## PVS Studio
|
||||
.PVS-Studio/
|
||||
project/target
|
||||
|
|
|
@ -70,7 +70,7 @@ task cleanjar(type: Jar, dependsOn: jar) {
|
|||
|
||||
dependencies {
|
||||
pack project(':LauncherAPI')
|
||||
bundle group: 'org.fusesource.jansi', name:'jansi', version: rootProject['verJansi']
|
||||
bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
||||
bundle group: 'org.jline', name: 'jline', version: rootProject['verJline']
|
||||
bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
|
||||
bundle group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline']
|
||||
|
@ -117,16 +117,18 @@ task hikari(type: Copy) {
|
|||
task launch4j(type: Copy) {
|
||||
duplicatesStrategy = 'EXCLUDE'
|
||||
into "$buildDir/libs/libraries/launch4j"
|
||||
from(configurations.launch4j.collect { it.isDirectory() ? it : ((it.getName().startsWith("launch4j") && it.getName().contains("workdir")) ? zipTree(it) : it) })
|
||||
from(configurations.launch4j.collect {
|
||||
it.isDirectory() ? it : ((it.getName().startsWith("launch4j") && it.getName().contains("workdir")) ? zipTree(it) : it)
|
||||
})
|
||||
includeEmptyDirs false
|
||||
eachFile { FileCopyDetails fcp ->
|
||||
if (fcp.relativePath.pathString.startsWith("launch4j-") &&
|
||||
fcp.relativePath.pathString.contains("workdir")) {
|
||||
def segments = fcp.relativePath.segments
|
||||
def pathSegments = segments[1..-1] as String[]
|
||||
fcp.relativePath = new RelativePath(!fcp.file.isDirectory(), pathSegments)
|
||||
} else if (fcp.relativePath.pathString.contains("META-INF")) fcp.exclude()
|
||||
fcp.mode = 0755
|
||||
if (fcp.relativePath.pathString.startsWith("launch4j-") &&
|
||||
fcp.relativePath.pathString.contains("workdir")) {
|
||||
def segments = fcp.relativePath.segments
|
||||
def pathSegments = segments[1..-1] as String[]
|
||||
fcp.relativePath = new RelativePath(!fcp.file.isDirectory(), pathSegments)
|
||||
} else if (fcp.relativePath.pathString.contains("META-INF")) fcp.exclude()
|
||||
fcp.mode = 0755
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import pro.gravit.launchserver.binary.*;
|
||||
import pro.gravit.launchserver.config.LaunchServerConfig;
|
||||
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
|
||||
import pro.gravit.launchserver.launchermodules.LauncherModuleLoader;
|
||||
import pro.gravit.launchserver.manangers.CertificateManager;
|
||||
import pro.gravit.launchserver.manangers.MirrorManager;
|
||||
import pro.gravit.launchserver.manangers.ReconfigurableManager;
|
||||
|
@ -51,215 +52,49 @@
|
|||
|
||||
public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable {
|
||||
|
||||
public enum ReloadType {
|
||||
NO_AUTH,
|
||||
NO_COMPONENTS,
|
||||
FULL
|
||||
}
|
||||
|
||||
public enum LaunchServerEnv {
|
||||
TEST,
|
||||
DEV,
|
||||
DEBUG,
|
||||
PRODUCTION
|
||||
}
|
||||
|
||||
public interface LaunchServerConfigManager {
|
||||
LaunchServerConfig readConfig() throws IOException;
|
||||
|
||||
LaunchServerRuntimeConfig readRuntimeConfig() throws IOException;
|
||||
|
||||
void writeConfig(LaunchServerConfig config) throws IOException;
|
||||
|
||||
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
|
||||
}
|
||||
|
||||
public void reload(ReloadType type) throws Exception {
|
||||
config.close(type);
|
||||
Map<String, AuthProviderPair> pairs = null;
|
||||
if (type.equals(ReloadType.NO_AUTH)) {
|
||||
pairs = config.auth;
|
||||
}
|
||||
LogHelper.info("Reading LaunchServer config file");
|
||||
config = launchServerConfigManager.readConfig();
|
||||
config.setLaunchServer(this);
|
||||
if (type.equals(ReloadType.NO_AUTH)) {
|
||||
config.auth = pairs;
|
||||
}
|
||||
config.verify();
|
||||
config.init(type);
|
||||
if (type.equals(ReloadType.FULL) && config.components != null) {
|
||||
LogHelper.debug("PreInit components");
|
||||
config.components.forEach((k, v) -> {
|
||||
LogHelper.subDebug("PreInit component %s", k);
|
||||
v.preInit(this);
|
||||
});
|
||||
LogHelper.debug("PreInit components successful");
|
||||
LogHelper.debug("Init components");
|
||||
config.components.forEach((k, v) -> {
|
||||
LogHelper.subDebug("Init component %s", k);
|
||||
v.init(this);
|
||||
});
|
||||
LogHelper.debug("Init components successful");
|
||||
LogHelper.debug("PostInit components");
|
||||
config.components.forEach((k, v) -> {
|
||||
LogHelper.subDebug("PostInit component %s", k);
|
||||
v.postInit(this);
|
||||
});
|
||||
LogHelper.debug("PostInit components successful");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Command> getCommands() {
|
||||
Map<String, Command> commands = new HashMap<>();
|
||||
SubCommand reload = new SubCommand() {
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
reload(ReloadType.FULL);
|
||||
return;
|
||||
}
|
||||
switch (args[0]) {
|
||||
case "full":
|
||||
reload(ReloadType.FULL);
|
||||
break;
|
||||
case "no_auth":
|
||||
reload(ReloadType.NO_AUTH);
|
||||
break;
|
||||
case "no_components":
|
||||
reload(ReloadType.NO_COMPONENTS);
|
||||
break;
|
||||
default:
|
||||
reload(ReloadType.FULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
commands.put("reload", reload);
|
||||
return commands;
|
||||
}
|
||||
|
||||
|
||||
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Collection<ClientProfile> result;
|
||||
|
||||
private ProfilesFileVisitor(Collection<ClientProfile> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
LogHelper.info("Syncing '%s' profile", IOHelper.getFileName(file));
|
||||
|
||||
// Read profile
|
||||
ClientProfile profile;
|
||||
try (BufferedReader reader = IOHelper.newReader(file)) {
|
||||
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
|
||||
}
|
||||
profile.verify();
|
||||
|
||||
// Add SIGNED profile to result list
|
||||
result.add(profile);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
// Constant paths
|
||||
|
||||
public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null;
|
||||
public final Path dir;
|
||||
|
||||
public final LaunchServerEnv env;
|
||||
|
||||
public final Path launcherLibraries;
|
||||
|
||||
public final Path launcherLibrariesCompile;
|
||||
|
||||
public final Path caCertFile;
|
||||
|
||||
// Constant paths
|
||||
public final Path caKeyFile;
|
||||
|
||||
public final Path serverCertFile;
|
||||
|
||||
public final Path serverKeyFile;
|
||||
|
||||
public final Path updatesDir;
|
||||
|
||||
public final LaunchServerConfigManager launchServerConfigManager;
|
||||
|
||||
//public static LaunchServer server = null;
|
||||
|
||||
public final Path profilesDir;
|
||||
// Server config
|
||||
|
||||
public LaunchServerConfig config;
|
||||
public final LaunchServerRuntimeConfig runtime;
|
||||
|
||||
|
||||
public final ECPublicKey publicKey;
|
||||
|
||||
public final ECPrivateKey privateKey;
|
||||
// Launcher binary
|
||||
|
||||
public final JARLauncherBinary launcherBinary;
|
||||
|
||||
//public static LaunchServer server = null;
|
||||
public final Class<? extends LauncherBinary> launcherEXEBinaryClass;
|
||||
|
||||
// Server config
|
||||
public final LauncherBinary launcherEXEBinary;
|
||||
// HWID ban + anti-brutforce
|
||||
|
||||
public final SessionManager sessionManager;
|
||||
|
||||
public final AuthHookManager authHookManager;
|
||||
// Server
|
||||
|
||||
public final LaunchServerModulesManager modulesManager;
|
||||
|
||||
// Launcher binary
|
||||
public final MirrorManager mirrorManager;
|
||||
|
||||
public final ReconfigurableManager reconfigurableManager;
|
||||
|
||||
public final ConfigManager configManager;
|
||||
|
||||
// HWID ban + anti-brutforce
|
||||
public final CertificateManager certificateManager;
|
||||
|
||||
public final ProguardConf proguardConf;
|
||||
|
||||
|
||||
// Server
|
||||
public final CommandHandler commandHandler;
|
||||
|
||||
public final NettyServerSocketHandler nettyServerSocketHandler;
|
||||
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
public final Timer taskPool;
|
||||
public final AtomicBoolean started = new AtomicBoolean(false);
|
||||
public final LauncherModuleLoader launcherModuleLoader;
|
||||
public LaunchServerConfig config;
|
||||
public volatile Map<String, HashedDir> updatesDirMap;
|
||||
// Updates and profiles
|
||||
private volatile List<ClientProfile> profilesList;
|
||||
public volatile Map<String, HashedDir> updatesDirMap;
|
||||
|
||||
public final Timer taskPool;
|
||||
|
||||
public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null;
|
||||
|
||||
public static class LaunchServerDirectories {
|
||||
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
|
||||
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
||||
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile";
|
||||
public Path updatesDir;
|
||||
public Path profilesDir;
|
||||
public Path launcherLibrariesDir;
|
||||
public Path launcherLibrariesCompileDir;
|
||||
public Path dir;
|
||||
public Path trustStore;
|
||||
|
||||
public void collect() {
|
||||
if (updatesDir == null) updatesDir = dir.resolve(UPDATES_NAME);
|
||||
if (profilesDir == null) profilesDir = dir.resolve(PROFILES_NAME);
|
||||
if (trustStore == null) trustStore = dir.resolve(TRUSTSTORE_NAME);
|
||||
if (launcherLibrariesDir == null) launcherLibrariesDir = dir.resolve(LAUNCHERLIBRARIES_NAME);
|
||||
if (launcherLibrariesCompileDir == null) launcherLibrariesCompileDir = dir.resolve(LAUNCHERLIBRARIESCOMPILE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey privateKey, CommandHandler commandHandler, CertificateManager certificateManager) throws IOException {
|
||||
this.dir = directories.dir;
|
||||
|
@ -365,7 +200,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
launcherBinary.init();
|
||||
launcherEXEBinary.init();
|
||||
syncLauncherBinaries();
|
||||
|
||||
launcherModuleLoader = new LauncherModuleLoader(this);
|
||||
// Sync updates dir
|
||||
if (!IOHelper.isDir(updatesDir))
|
||||
Files.createDirectory(updatesDir);
|
||||
|
@ -375,7 +210,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
if (!IOHelper.isDir(profilesDir))
|
||||
Files.createDirectory(profilesDir);
|
||||
syncProfilesDir();
|
||||
|
||||
launcherModuleLoader.init();
|
||||
nettyServerSocketHandler = new NettyServerSocketHandler(this);
|
||||
// post init modules
|
||||
modulesManager.invokeEvent(new LaunchServerPostInitPhase(this));
|
||||
|
@ -389,6 +224,73 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
}
|
||||
}
|
||||
|
||||
public void reload(ReloadType type) throws Exception {
|
||||
config.close(type);
|
||||
Map<String, AuthProviderPair> pairs = null;
|
||||
if (type.equals(ReloadType.NO_AUTH)) {
|
||||
pairs = config.auth;
|
||||
}
|
||||
LogHelper.info("Reading LaunchServer config file");
|
||||
config = launchServerConfigManager.readConfig();
|
||||
config.setLaunchServer(this);
|
||||
if (type.equals(ReloadType.NO_AUTH)) {
|
||||
config.auth = pairs;
|
||||
}
|
||||
config.verify();
|
||||
config.init(type);
|
||||
if (type.equals(ReloadType.FULL) && config.components != null) {
|
||||
LogHelper.debug("PreInit components");
|
||||
config.components.forEach((k, v) -> {
|
||||
LogHelper.subDebug("PreInit component %s", k);
|
||||
v.preInit(this);
|
||||
});
|
||||
LogHelper.debug("PreInit components successful");
|
||||
LogHelper.debug("Init components");
|
||||
config.components.forEach((k, v) -> {
|
||||
LogHelper.subDebug("Init component %s", k);
|
||||
v.init(this);
|
||||
});
|
||||
LogHelper.debug("Init components successful");
|
||||
LogHelper.debug("PostInit components");
|
||||
config.components.forEach((k, v) -> {
|
||||
LogHelper.subDebug("PostInit component %s", k);
|
||||
v.postInit(this);
|
||||
});
|
||||
LogHelper.debug("PostInit components successful");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Command> getCommands() {
|
||||
Map<String, Command> commands = new HashMap<>();
|
||||
SubCommand reload = new SubCommand() {
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
reload(ReloadType.FULL);
|
||||
return;
|
||||
}
|
||||
switch (args[0]) {
|
||||
case "full":
|
||||
reload(ReloadType.FULL);
|
||||
break;
|
||||
case "no_auth":
|
||||
reload(ReloadType.NO_AUTH);
|
||||
break;
|
||||
case "no_components":
|
||||
reload(ReloadType.NO_COMPONENTS);
|
||||
break;
|
||||
default:
|
||||
reload(ReloadType.FULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
commands.put("reload", reload);
|
||||
return commands;
|
||||
}
|
||||
|
||||
private LauncherBinary binary() {
|
||||
if (launcherEXEBinaryClass != null) {
|
||||
try {
|
||||
|
@ -406,7 +308,6 @@ private LauncherBinary binary() {
|
|||
return new EXELauncherBinary(this);
|
||||
}
|
||||
|
||||
|
||||
public void buildLauncherBinaries() throws IOException {
|
||||
launcherBinary.build();
|
||||
launcherEXEBinary.build();
|
||||
|
@ -435,7 +336,6 @@ public HashedDir getUpdateDir(String name) {
|
|||
return updatesDirMap.get(name);
|
||||
}
|
||||
|
||||
|
||||
public Set<Entry<String, HashedDir>> getUpdateDirs() {
|
||||
return updatesDirMap.entrySet();
|
||||
}
|
||||
|
@ -467,7 +367,6 @@ public void run() {
|
|||
modulesManager.invokeEvent(new LaunchServerFullInitEvent(this));
|
||||
}
|
||||
|
||||
|
||||
public void syncLauncherBinaries() throws IOException {
|
||||
LogHelper.info("Syncing launcher binaries");
|
||||
|
||||
|
@ -482,7 +381,6 @@ public void syncLauncherBinaries() throws IOException {
|
|||
|
||||
}
|
||||
|
||||
|
||||
public void syncProfilesDir() throws IOException {
|
||||
LogHelper.info("Syncing profiles dir");
|
||||
List<ClientProfile> newProfies = new LinkedList<>();
|
||||
|
@ -493,7 +391,6 @@ public void syncProfilesDir() throws IOException {
|
|||
profilesList = Collections.unmodifiableList(newProfies);
|
||||
}
|
||||
|
||||
|
||||
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
||||
LogHelper.info("Syncing updates dir");
|
||||
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
|
||||
|
@ -565,4 +462,73 @@ public void fullyRestart() {
|
|||
restart();
|
||||
JVMHelper.RUNTIME.exit(0);
|
||||
}
|
||||
|
||||
|
||||
public enum ReloadType {
|
||||
NO_AUTH,
|
||||
NO_COMPONENTS,
|
||||
FULL
|
||||
}
|
||||
|
||||
public enum LaunchServerEnv {
|
||||
TEST,
|
||||
DEV,
|
||||
DEBUG,
|
||||
PRODUCTION
|
||||
}
|
||||
|
||||
public interface LaunchServerConfigManager {
|
||||
LaunchServerConfig readConfig() throws IOException;
|
||||
|
||||
LaunchServerRuntimeConfig readRuntimeConfig() throws IOException;
|
||||
|
||||
void writeConfig(LaunchServerConfig config) throws IOException;
|
||||
|
||||
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
|
||||
}
|
||||
|
||||
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Collection<ClientProfile> result;
|
||||
|
||||
private ProfilesFileVisitor(Collection<ClientProfile> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
LogHelper.info("Syncing '%s' profile", IOHelper.getFileName(file));
|
||||
|
||||
// Read profile
|
||||
ClientProfile profile;
|
||||
try (BufferedReader reader = IOHelper.newReader(file)) {
|
||||
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
|
||||
}
|
||||
profile.verify();
|
||||
|
||||
// Add SIGNED profile to result list
|
||||
result.add(profile);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LaunchServerDirectories {
|
||||
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
|
||||
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
||||
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile";
|
||||
public Path updatesDir;
|
||||
public Path profilesDir;
|
||||
public Path launcherLibrariesDir;
|
||||
public Path launcherLibrariesCompileDir;
|
||||
public Path dir;
|
||||
public Path trustStore;
|
||||
|
||||
public void collect() {
|
||||
if (updatesDir == null) updatesDir = dir.resolve(UPDATES_NAME);
|
||||
if (profilesDir == null) profilesDir = dir.resolve(PROFILES_NAME);
|
||||
if (trustStore == null) trustStore = dir.resolve(TRUSTSTORE_NAME);
|
||||
if (launcherLibrariesDir == null) launcherLibrariesDir = dir.resolve(LAUNCHERLIBRARIES_NAME);
|
||||
if (launcherLibrariesCompileDir == null)
|
||||
launcherLibrariesCompileDir = dir.resolve(LAUNCHERLIBRARIESCOMPILE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||
|
@ -23,7 +24,6 @@
|
|||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
|
@ -177,9 +177,9 @@ public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOExcept
|
|||
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
|
||||
directories.dir = dir;
|
||||
if (inDocker) {
|
||||
Path parentLibraries = StarterAgent.libraries.toAbsolutePath().normalize().getParent();
|
||||
directories.launcherLibrariesCompileDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIESCOMPILE_NAME);
|
||||
directories.launcherLibrariesDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIES_NAME);
|
||||
Path parentLibraries = StarterAgent.libraries.toAbsolutePath().normalize().getParent();
|
||||
directories.launcherLibrariesCompileDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIESCOMPILE_NAME);
|
||||
directories.launcherLibrariesDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIES_NAME);
|
||||
}
|
||||
LaunchServer server = new LaunchServerBuilder()
|
||||
.setDirectories(directories)
|
||||
|
|
|
@ -6,15 +6,30 @@
|
|||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.PosixFileAttributeView;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public final class StarterAgent {
|
||||
|
||||
public static Instrumentation inst = null;
|
||||
public static Path libraries = null;
|
||||
private static boolean isStarted = false;
|
||||
|
||||
public static boolean isAgentStarted() {
|
||||
return isStarted;
|
||||
}
|
||||
|
||||
public static void premain(String agentArgument, Instrumentation inst) {
|
||||
StarterAgent.inst = inst;
|
||||
libraries = Paths.get(Optional.ofNullable(agentArgument).map(e -> e.trim()).filter(e -> !e.isEmpty()).orElse("libraries"));
|
||||
isStarted = true;
|
||||
try {
|
||||
Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StarterVisitor extends SimpleFileVisitor<Path> {
|
||||
private static final Set<PosixFilePermission> DPERMS;
|
||||
|
||||
|
@ -49,23 +64,4 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
|||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public static Instrumentation inst = null;
|
||||
public static Path libraries = null;
|
||||
private static boolean isStarted = false;
|
||||
|
||||
public static boolean isAgentStarted() {
|
||||
return isStarted;
|
||||
}
|
||||
|
||||
public static void premain(String agentArgument, Instrumentation inst) {
|
||||
StarterAgent.inst = inst;
|
||||
libraries = Paths.get(Optional.ofNullable(agentArgument).map(e -> e.trim()).filter(e -> !e.isEmpty()).orElse("libraries"));
|
||||
isStarted = true;
|
||||
try {
|
||||
Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,35 +18,20 @@
|
|||
* чего угодно. Работает через поиск class-файлов в classpath.
|
||||
*/
|
||||
public class ClassMetadataReader implements Closeable {
|
||||
private static class CheckSuperClassVisitor extends ClassVisitor {
|
||||
|
||||
String superClassName;
|
||||
|
||||
public CheckSuperClassVisitor() {
|
||||
super(Opcodes.ASM7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName,
|
||||
String[] interfaces) {
|
||||
superClassName = superName;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<JarFile> cp;
|
||||
|
||||
public ClassMetadataReader(List<JarFile> cp) {
|
||||
this.cp = cp;
|
||||
}
|
||||
|
||||
public List<JarFile> getCp() {
|
||||
return cp;
|
||||
}
|
||||
|
||||
public ClassMetadataReader() {
|
||||
this.cp = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<JarFile> getCp() {
|
||||
return cp;
|
||||
}
|
||||
|
||||
public void acceptVisitor(byte[] classData, ClassVisitor visitor) {
|
||||
new ClassReader(classData).accept(visitor, 0);
|
||||
}
|
||||
|
@ -63,7 +48,6 @@ public void acceptVisitor(String className, ClassVisitor visitor, int flags) thr
|
|||
acceptVisitor(getClassData(className), visitor, flags);
|
||||
}
|
||||
|
||||
|
||||
public byte[] getClassData(String className) throws IOException {
|
||||
for (JarFile f : cp) {
|
||||
if (f.getEntry(className + ".class") != null) {
|
||||
|
@ -111,4 +95,19 @@ public void close() {
|
|||
cp.clear();
|
||||
}
|
||||
|
||||
private static class CheckSuperClassVisitor extends ClassVisitor {
|
||||
|
||||
String superClassName;
|
||||
|
||||
public CheckSuperClassVisitor() {
|
||||
super(Opcodes.ASM7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName,
|
||||
String[] interfaces) {
|
||||
superClassName = superName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,245 +1,243 @@
|
|||
package pro.gravit.launchserver.asm;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import pro.gravit.launchserver.binary.BuildContext;
|
||||
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
|
||||
import pro.gravit.launcher.LauncherInject;
|
||||
import pro.gravit.launcher.LauncherInjectionConstructor;
|
||||
import pro.gravit.launchserver.binary.BuildContext;
|
||||
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {
|
||||
private final Map<String, Object> values;
|
||||
private static final List<Class<?>> primitiveLDCClasses = Arrays.asList(java.lang.Integer.class, java.lang.Long.class,
|
||||
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
|
||||
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
|
||||
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
|
||||
private static final List<String> primitiveLDCDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
|
||||
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
|
||||
private static final Map<Class<?>, Serializer<?>> serializers;
|
||||
|
||||
public InjectClassAcceptor(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
static {
|
||||
serializers = new HashMap<>();
|
||||
serializers.put(List.class, new ListSerializer());
|
||||
serializers.put(Map.class, new MapSerializer());
|
||||
serializers.put(byte[].class, new ByteArraySerializer());
|
||||
serializers.put(Short.class, serializerClass(Opcodes.I2S));
|
||||
serializers.put(Byte.class, serializerClass(Opcodes.I2B));
|
||||
serializers.put(Type.class, (Serializer<Type>) e -> { // ow.Type == java.lang.Class in LDC
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(new LdcInsnNode(e));
|
||||
return ret;
|
||||
});
|
||||
serializers.put(Boolean.class, (Serializer<Boolean>) e -> {
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(new InsnNode(e ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
|
||||
return ret;
|
||||
});
|
||||
serializers.put(Character.class, (Serializer<Character>) e -> {
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(NodeUtils.push((int) e));
|
||||
ret.add(new InsnNode(Opcodes.I2C));
|
||||
return ret;
|
||||
});
|
||||
serializers.put(Enum.class, (Serializer<Enum>) NodeUtils::makeValueEnumGetter);
|
||||
}
|
||||
|
||||
private static final List<Class<?>> primitiveLDCClasses = Arrays.asList(java.lang.Integer.class, java.lang.Long.class,
|
||||
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
|
||||
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
|
||||
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
|
||||
private static final List<String> primitiveLDCDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
|
||||
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
|
||||
private final Map<String, Object> values;
|
||||
|
||||
private static void visit(ClassNode classNode, Map<String, Object> values) {
|
||||
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name))
|
||||
.findFirst().orElseGet(() -> {
|
||||
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
|
||||
"<clinit>", "()V", null, null);
|
||||
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
classNode.methods.add(newClinitMethod);
|
||||
return newClinitMethod;
|
||||
});
|
||||
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
|
||||
.collect(Collectors.toList());
|
||||
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
|
||||
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
|
||||
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
|
||||
classNode.fields.forEach(field -> {
|
||||
// Notice that fields that will be used with this algo should not have default
|
||||
// value by = ...;
|
||||
AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
|
||||
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
|
||||
.orElse(null) : null;
|
||||
if (valueAnnotation == null) {
|
||||
return;
|
||||
}
|
||||
field.invisibleAnnotations.remove(valueAnnotation);
|
||||
AtomicReference<String> valueName = new AtomicReference<String>(null);
|
||||
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
|
||||
@Override
|
||||
public void visit(final String name, final Object value) {
|
||||
if ("value".equals(name)) {
|
||||
if (value.getClass() != String.class)
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid annotation with value class %s", field.getClass().getName()));
|
||||
valueName.set(value.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (valueName.get() == null) {
|
||||
throw new IllegalArgumentException("Annotation should always contains 'value' key");
|
||||
}
|
||||
if (!values.containsKey(valueName.get())) {
|
||||
return;
|
||||
}
|
||||
Object value = values.get(valueName.get());
|
||||
if ((field.access & Opcodes.ACC_STATIC) != 0) {
|
||||
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
|
||||
field.value = value;
|
||||
return;
|
||||
}
|
||||
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray())
|
||||
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
|
||||
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
|
||||
InsnList setter = serializeValue(value);
|
||||
if (putStaticNodes.isEmpty()) {
|
||||
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
|
||||
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
|
||||
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter));
|
||||
} else {
|
||||
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
|
||||
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
|
||||
clinitMethod.instructions.insertBefore(fieldInsnNode, setter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (initMethod == null) {
|
||||
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name));
|
||||
}
|
||||
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
|
||||
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
|
||||
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
|
||||
InsnList setter = serializeValue(value);
|
||||
if (putFieldNodes.isEmpty()) {
|
||||
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
|
||||
Arrays.stream(initMethod.instructions.toArray())
|
||||
.filter(node -> node.getOpcode() == Opcodes.RETURN)
|
||||
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
|
||||
} else {
|
||||
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
|
||||
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
|
||||
initMethod.instructions.insertBefore(fieldInsnNode, setter);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
public InjectClassAcceptor(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, Serializer<?>> serializers;
|
||||
private static void visit(ClassNode classNode, Map<String, Object> values) {
|
||||
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name))
|
||||
.findFirst().orElseGet(() -> {
|
||||
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
|
||||
"<clinit>", "()V", null, null);
|
||||
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
classNode.methods.add(newClinitMethod);
|
||||
return newClinitMethod;
|
||||
});
|
||||
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
|
||||
.collect(Collectors.toList());
|
||||
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
|
||||
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
|
||||
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
|
||||
classNode.fields.forEach(field -> {
|
||||
// Notice that fields that will be used with this algo should not have default
|
||||
// value by = ...;
|
||||
AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
|
||||
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
|
||||
.orElse(null) : null;
|
||||
if (valueAnnotation == null) {
|
||||
return;
|
||||
}
|
||||
field.invisibleAnnotations.remove(valueAnnotation);
|
||||
AtomicReference<String> valueName = new AtomicReference<String>(null);
|
||||
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
|
||||
@Override
|
||||
public void visit(final String name, final Object value) {
|
||||
if ("value".equals(name)) {
|
||||
if (value.getClass() != String.class)
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid annotation with value class %s", field.getClass().getName()));
|
||||
valueName.set(value.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (valueName.get() == null) {
|
||||
throw new IllegalArgumentException("Annotation should always contains 'value' key");
|
||||
}
|
||||
if (!values.containsKey(valueName.get())) {
|
||||
return;
|
||||
}
|
||||
Object value = values.get(valueName.get());
|
||||
if ((field.access & Opcodes.ACC_STATIC) != 0) {
|
||||
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
|
||||
field.value = value;
|
||||
return;
|
||||
}
|
||||
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray())
|
||||
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
|
||||
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
|
||||
InsnList setter = serializeValue(value);
|
||||
if (putStaticNodes.isEmpty()) {
|
||||
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
|
||||
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
|
||||
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter));
|
||||
} else {
|
||||
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
|
||||
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
|
||||
clinitMethod.instructions.insertBefore(fieldInsnNode, setter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (initMethod == null) {
|
||||
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name));
|
||||
}
|
||||
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
|
||||
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
|
||||
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
|
||||
InsnList setter = serializeValue(value);
|
||||
if (putFieldNodes.isEmpty()) {
|
||||
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
|
||||
Arrays.stream(initMethod.instructions.toArray())
|
||||
.filter(node -> node.getOpcode() == Opcodes.RETURN)
|
||||
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
|
||||
} else {
|
||||
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
|
||||
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
|
||||
initMethod.instructions.insertBefore(fieldInsnNode, setter);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static {
|
||||
serializers = new HashMap<>();
|
||||
serializers.put(List.class, new ListSerializer());
|
||||
serializers.put(Map.class, new MapSerializer());
|
||||
serializers.put(byte[].class, new ByteArraySerializer());
|
||||
serializers.put(Short.class, serializerClass(Opcodes.I2S));
|
||||
serializers.put(Byte.class, serializerClass(Opcodes.I2B));
|
||||
serializers.put(Type.class, (Serializer<Type>) e -> { // ow.Type == java.lang.Class in LDC
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(new LdcInsnNode(e));
|
||||
return ret;
|
||||
});
|
||||
serializers.put(Boolean.class, (Serializer<Boolean>) e -> {
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(new InsnNode(e ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
|
||||
return ret;
|
||||
});
|
||||
serializers.put(Character.class, (Serializer<Character>) e -> {
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(NodeUtils.push((int) e));
|
||||
ret.add(new InsnNode(Opcodes.I2C));
|
||||
return ret;
|
||||
});
|
||||
serializers.put(Enum.class, (Serializer<Enum>) NodeUtils::makeValueEnumGetter);
|
||||
}
|
||||
private static Serializer<?> serializerClass(int opcode) {
|
||||
return new Serializer<Number>() {
|
||||
@Override
|
||||
public InsnList serialize(Number value) {
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(NodeUtils.push(value.intValue()));
|
||||
ret.add(new InsnNode(opcode));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static Serializer<?> serializerClass(int opcode) {
|
||||
return new Serializer<Number>() {
|
||||
@Override
|
||||
public InsnList serialize(Number value) {
|
||||
InsnList ret = new InsnList();
|
||||
ret.add(NodeUtils.push(((Number) value).intValue()));
|
||||
ret.add(new InsnNode(opcode));
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Serializer<T> {
|
||||
InsnList serialize(T value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static InsnList serializeValue(Object value) {
|
||||
if (value == null) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
return insnList;
|
||||
}
|
||||
if (primitiveLDCClasses.contains(value.getClass())) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new LdcInsnNode(value));
|
||||
return insnList;
|
||||
}
|
||||
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
|
||||
if (serializerEntry.getKey().isInstance(value)) {
|
||||
return ((Serializer) serializerEntry.getValue()).serialize(value);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported",
|
||||
value.getClass()));
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
private static InsnList serializeValue(Object value) {
|
||||
if (value == null) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
return insnList;
|
||||
}
|
||||
if (primitiveLDCClasses.contains(value.getClass())) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new LdcInsnNode(value));
|
||||
return insnList;
|
||||
}
|
||||
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
|
||||
if (serializerEntry.getKey().isInstance(value)) {
|
||||
return ((Serializer) serializerEntry.getValue()).serialize(value);
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported",
|
||||
value.getClass()));
|
||||
}
|
||||
|
||||
private static class ListSerializer implements Serializer<List> {
|
||||
@Override
|
||||
public InsnList serialize(List value) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(NodeUtils.push(value.size()));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false));
|
||||
for (Object object : value) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(serializeValue(object));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true));
|
||||
insnList.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void transform(ClassNode classNode, String className, BuildContext context) {
|
||||
visit(classNode, values);
|
||||
}
|
||||
|
||||
private static class MapSerializer implements Serializer<Map> {
|
||||
@Override
|
||||
public InsnList serialize(Map value) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass())));
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), false));
|
||||
for (Object entryObject : value.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) entryObject;
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(serializeValue(entry.getKey()));
|
||||
insnList.add(serializeValue(entry.getValue()));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)),
|
||||
true));
|
||||
insnList.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
@FunctionalInterface
|
||||
private interface Serializer<T> {
|
||||
InsnList serialize(T value);
|
||||
}
|
||||
|
||||
private static class ByteArraySerializer implements Serializer<byte[]> {
|
||||
@Override
|
||||
public InsnList serialize(byte[] value) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class),
|
||||
"getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false));
|
||||
insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value)));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class),
|
||||
"decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)),
|
||||
false));
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
private static class ListSerializer implements Serializer<List> {
|
||||
@Override
|
||||
public InsnList serialize(List value) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(NodeUtils.push(value.size()));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false));
|
||||
for (Object object : value) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(serializeValue(object));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true));
|
||||
insnList.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(ClassNode classNode, String className, BuildContext context) {
|
||||
visit(classNode, values);
|
||||
}
|
||||
private static class MapSerializer implements Serializer<Map> {
|
||||
@Override
|
||||
public InsnList serialize(Map value) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass())));
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), false));
|
||||
for (Object entryObject : value.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) entryObject;
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
insnList.add(serializeValue(entry.getKey()));
|
||||
insnList.add(serializeValue(entry.getValue()));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)),
|
||||
true));
|
||||
insnList.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ByteArraySerializer implements Serializer<byte[]> {
|
||||
@Override
|
||||
public InsnList serialize(byte[] value) {
|
||||
InsnList insnList = new InsnList();
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class),
|
||||
"getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false));
|
||||
insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value)));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class),
|
||||
"decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)),
|
||||
false));
|
||||
return insnList;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
public final class NodeUtils {
|
||||
|
||||
public static final int MAX_SAFE_BYTE_COUNT = 65535 - Byte.MAX_VALUE;
|
||||
|
||||
private NodeUtils() {
|
||||
}
|
||||
|
||||
|
@ -186,8 +188,6 @@ public static InsnList getSafeStringInsnList(String string) {
|
|||
return insnList;
|
||||
}
|
||||
|
||||
public static final int MAX_SAFE_BYTE_COUNT = 65535 - Byte.MAX_VALUE;
|
||||
|
||||
public static String[] splitUtf8ToChunks(String text, int maxBytes) {
|
||||
List<String> parts = new ArrayList<>();
|
||||
|
||||
|
@ -238,9 +238,9 @@ else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
|
|||
}
|
||||
|
||||
public static InsnList makeValueEnumGetter(@SuppressWarnings("rawtypes") Enum u) {
|
||||
InsnList ret = new InsnList();
|
||||
Type e = Type.getType(u.getClass());
|
||||
ret.add(new FieldInsnNode(Opcodes.GETSTATIC, e.getInternalName(), u.name(), e.getDescriptor()));
|
||||
return ret;
|
||||
}
|
||||
InsnList ret = new InsnList();
|
||||
Type e = Type.getType(u.getClass());
|
||||
ret.add(new FieldInsnNode(Opcodes.GETSTATIC, e.getInternalName(), u.name(), e.getDescriptor()));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
import java.util.Map;
|
||||
|
||||
public class AuthProviderPair {
|
||||
public final boolean isDefault = true;
|
||||
public AuthProvider provider;
|
||||
public AuthHandler handler;
|
||||
public TextureProvider textureProvider;
|
||||
public Map<String, String> links;
|
||||
public transient String name;
|
||||
public String displayName;
|
||||
public final boolean isDefault = true;
|
||||
|
||||
public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvider textureProvider) {
|
||||
this.provider = provider;
|
||||
|
@ -24,35 +24,33 @@ public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvi
|
|||
}
|
||||
|
||||
public void init(LaunchServer srv, String name) {
|
||||
this.name = name;
|
||||
if(links != null) link(srv);
|
||||
if(provider == null) throw new NullPointerException(String.format("Auth %s provider null", name));
|
||||
if(handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
|
||||
if(textureProvider == null) throw new NullPointerException(String.format("Auth %s textureProvider null", name));
|
||||
this.name = name;
|
||||
if (links != null) link(srv);
|
||||
if (provider == null) throw new NullPointerException(String.format("Auth %s provider null", name));
|
||||
if (handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
|
||||
if (textureProvider == null)
|
||||
throw new NullPointerException(String.format("Auth %s textureProvider null", name));
|
||||
provider.init(srv);
|
||||
handler.init(srv);
|
||||
}
|
||||
public void link(LaunchServer srv)
|
||||
{
|
||||
links.forEach((k,v) -> {
|
||||
|
||||
public void link(LaunchServer srv) {
|
||||
links.forEach((k, v) -> {
|
||||
AuthProviderPair pair = srv.config.getAuthProviderPair(v);
|
||||
if(pair == null)
|
||||
{
|
||||
if (pair == null) {
|
||||
throw new NullPointerException(String.format("Auth %s link failed. Pair %s not found", name, v));
|
||||
}
|
||||
if("provider".equals(k))
|
||||
{
|
||||
if(pair.provider == null) throw new NullPointerException(String.format("Auth %s link failed. %s.provider is null", name, v));
|
||||
if ("provider".equals(k)) {
|
||||
if (pair.provider == null)
|
||||
throw new NullPointerException(String.format("Auth %s link failed. %s.provider is null", name, v));
|
||||
provider = pair.provider;
|
||||
}
|
||||
else if("handler".equals(k))
|
||||
{
|
||||
if(pair.handler == null) throw new NullPointerException(String.format("Auth %s link failed. %s.handler is null", name, v));
|
||||
} else if ("handler".equals(k)) {
|
||||
if (pair.handler == null)
|
||||
throw new NullPointerException(String.format("Auth %s link failed. %s.handler is null", name, v));
|
||||
handler = pair.handler;
|
||||
}
|
||||
else if("textureProvider".equals(k))
|
||||
{
|
||||
if(pair.textureProvider == null) throw new NullPointerException(String.format("Auth %s link failed. %s.textureProvider is null", name, v));
|
||||
} else if ("textureProvider".equals(k)) {
|
||||
if (pair.textureProvider == null)
|
||||
throw new NullPointerException(String.format("Auth %s link failed. %s.textureProvider is null", name, v));
|
||||
textureProvider = pair.textureProvider;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -21,8 +21,8 @@ public final class PostgreSQLSourceConfig implements AutoCloseable {
|
|||
private String poolName;
|
||||
|
||||
// Config
|
||||
private String address;
|
||||
private int port;
|
||||
private String[] addresses;
|
||||
private int[] ports;
|
||||
private String username;
|
||||
private String password;
|
||||
private String database;
|
||||
|
@ -43,8 +43,8 @@ public synchronized Connection getConnection() throws SQLException {
|
|||
PGSimpleDataSource postgresqlSource = new PGSimpleDataSource();
|
||||
|
||||
// Set credentials
|
||||
postgresqlSource.setServerNames(new String[] {address}); //TODO support multinode PostgreSQL DB
|
||||
postgresqlSource.setPortNumbers(new int[] {port});
|
||||
postgresqlSource.setServerNames(addresses);
|
||||
postgresqlSource.setPortNumbers(ports);
|
||||
postgresqlSource.setUser(username);
|
||||
postgresqlSource.setPassword(password);
|
||||
postgresqlSource.setDatabaseName(database);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
public abstract class AuthHandler implements AutoCloseable {
|
||||
public static final ProviderMap<AuthHandler> providers = new ProviderMap<>("AuthHandler");
|
||||
private static boolean registredHandl = false;
|
||||
|
||||
protected transient LaunchServer srv;
|
||||
|
||||
public static UUID authError(String message) throws AuthException {
|
||||
throw new AuthException(message);
|
||||
|
@ -31,8 +31,6 @@ public static void registerHandlers() {
|
|||
}
|
||||
}
|
||||
|
||||
protected transient LaunchServer srv;
|
||||
|
||||
/**
|
||||
* Returns the UUID associated with the account
|
||||
*
|
||||
|
|
|
@ -18,26 +18,8 @@
|
|||
import java.util.UUID;
|
||||
|
||||
public abstract class CachedAuthHandler extends AuthHandler implements NeedGarbageCollection, Reconfigurable {
|
||||
public static final class Entry {
|
||||
|
||||
public final UUID uuid;
|
||||
private String username;
|
||||
private String accessToken;
|
||||
private String serverID;
|
||||
|
||||
|
||||
public Entry(UUID uuid, String username, String accessToken, String serverID) {
|
||||
this.uuid = Objects.requireNonNull(uuid, "uuid");
|
||||
this.username = Objects.requireNonNull(username, "username");
|
||||
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
|
||||
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class EntryAndUsername {
|
||||
public Map<UUID, CachedAuthHandler.Entry> entryCache;
|
||||
public Map<String, UUID> usernameCache;
|
||||
}
|
||||
private transient final Map<UUID, Entry> entryCache = new HashMap<>(1024);
|
||||
private transient final Map<String, UUID> usernamesCache = new HashMap<>(1024);
|
||||
|
||||
@Override
|
||||
public Map<String, Command> getCommands() {
|
||||
|
@ -91,10 +73,6 @@ public void invoke(String... args) throws Exception {
|
|||
return commands;
|
||||
}
|
||||
|
||||
private transient final Map<UUID, Entry> entryCache = new HashMap<>(1024);
|
||||
private transient final Map<String, UUID> usernamesCache = new HashMap<>(1024);
|
||||
|
||||
|
||||
protected void addEntry(Entry entry) {
|
||||
Entry previous = entryCache.put(entry.uuid, entry);
|
||||
if (previous != null)
|
||||
|
@ -122,10 +100,8 @@ public synchronized UUID checkServer(String username, String serverID) throws IO
|
|||
serverID.equals(entry.serverID) ? entry.uuid : null;
|
||||
}
|
||||
|
||||
|
||||
protected abstract Entry fetchEntry(String username) throws IOException;
|
||||
|
||||
|
||||
protected abstract Entry fetchEntry(UUID uuid) throws IOException;
|
||||
|
||||
private Entry getEntry(String username) throws IOException {
|
||||
|
@ -187,7 +163,6 @@ public void loadUsernameCache(Map<String, UUID> map) {
|
|||
|
||||
protected abstract boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException;
|
||||
|
||||
|
||||
protected abstract boolean updateServerID(UUID uuid, String serverID) throws IOException;
|
||||
|
||||
@Override
|
||||
|
@ -201,4 +176,25 @@ public final synchronized String uuidToUsername(UUID uuid) throws IOException {
|
|||
Entry entry = getEntry(uuid);
|
||||
return entry == null ? null : entry.username;
|
||||
}
|
||||
|
||||
public static final class Entry {
|
||||
|
||||
public final UUID uuid;
|
||||
private String username;
|
||||
private String accessToken;
|
||||
private String serverID;
|
||||
|
||||
|
||||
public Entry(UUID uuid, String username, String accessToken, String serverID) {
|
||||
this.uuid = Objects.requireNonNull(uuid, "uuid");
|
||||
this.username = Objects.requireNonNull(username, "username");
|
||||
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
|
||||
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class EntryAndUsername {
|
||||
public Map<UUID, CachedAuthHandler.Entry> entryCache;
|
||||
public Map<String, UUID> usernameCache;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package pro.gravit.launchserver.auth.handler;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.HTTPRequest;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
@ -12,6 +12,31 @@ public class JsonAuthHandler extends CachedAuthHandler {
|
|||
public URL updateAuthUrl;
|
||||
public URL updateServerIdUrl;
|
||||
|
||||
@Override
|
||||
protected Entry fetchEntry(String username) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUsername(username)), getUrl), Entry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry fetchEntry(UUID uuid) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUUID(uuid)), getUrl), Entry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken)), updateAuthUrl), SuccessResponse.class).success;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateServerIDRequest(uuid, serverID)), updateServerIdUrl), SuccessResponse.class).success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public static class EntryRequestByUsername {
|
||||
public final String username;
|
||||
|
||||
|
@ -53,29 +78,4 @@ public UpdateServerIDRequest(UUID uuid, String serverID) {
|
|||
public static class SuccessResponse {
|
||||
public boolean success;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry fetchEntry(String username) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUsername(username)), getUrl), Entry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry fetchEntry(UUID uuid) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUUID(uuid)), getUrl), Entry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken)), updateAuthUrl), SuccessResponse.class).success;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
|
||||
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateServerIDRequest(uuid, serverID)), updateServerIdUrl), SuccessResponse.class).success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,14 +10,12 @@
|
|||
import java.util.UUID;
|
||||
|
||||
public final class RequestAuthHandler extends CachedAuthHandler {
|
||||
private String usernameFetch;
|
||||
private String uuidFetch;
|
||||
|
||||
private String updateAuth;
|
||||
private String updateServerID;
|
||||
|
||||
private final String splitSymbol = ":";
|
||||
private final String goodResponse = "OK";
|
||||
private String usernameFetch;
|
||||
private String uuidFetch;
|
||||
private String updateAuth;
|
||||
private String updateServerID;
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer srv) {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
public class AdvancedProtectHandler extends ProtectHandler implements SecureProtectHandler {
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package pro.gravit.launchserver.auth.protect;
|
||||
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
public class NoProtectHandler extends ProtectHandler {
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -14,6 +13,7 @@
|
|||
public class StdProtectHandler extends ProtectHandler implements ProfilesProtectHandler {
|
||||
public Map<String, List<String>> profileWhitelist = new HashMap<>();
|
||||
public List<String> allowUpdates = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
|
||||
return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign;
|
||||
|
@ -36,13 +36,12 @@ public boolean canChangeProfile(ClientProfile profile, Client client) {
|
|||
|
||||
@Override
|
||||
public boolean canGetUpdates(String updatesDirName, Client client) {
|
||||
return client.profile != null && ( client.profile.getDir().equals(updatesDirName) || client.profile.getAssetDir().equals(updatesDirName) || allowUpdates.contains(updatesDirName));
|
||||
return client.profile != null && (client.profile.getDir().equals(updatesDirName) || client.profile.getAssetDir().equals(updatesDirName) || allowUpdates.contains(updatesDirName));
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String profileTitle, String username)
|
||||
{
|
||||
public boolean isWhitelisted(String profileTitle, String username) {
|
||||
List<String> allowedUsername = profileWhitelist.get(profileTitle);
|
||||
if(allowedUsername == null) return true;
|
||||
if (allowedUsername == null) return true;
|
||||
return allowedUsername.contains(username);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,20 +4,19 @@
|
|||
import pro.gravit.launchserver.socket.Client;
|
||||
|
||||
public interface ProfilesProtectHandler {
|
||||
default boolean canGetProfiles(Client client)
|
||||
{
|
||||
default boolean canGetProfiles(Client client) {
|
||||
return true;
|
||||
}
|
||||
default boolean canGetProfile(ClientProfile profile, Client client)
|
||||
{
|
||||
|
||||
default boolean canGetProfile(ClientProfile profile, Client client) {
|
||||
return true;
|
||||
}
|
||||
default boolean canChangeProfile(ClientProfile profile, Client client)
|
||||
{
|
||||
|
||||
default boolean canChangeProfile(ClientProfile profile, Client client) {
|
||||
return client.isAuth;
|
||||
}
|
||||
default boolean canGetUpdates(String updatesDirName, Client client)
|
||||
{
|
||||
|
||||
default boolean canGetUpdates(String updatesDirName, Client client) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,21 +12,23 @@
|
|||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
public interface SecureProtectHandler {
|
||||
default byte[] generateSecureLevelKey()
|
||||
{
|
||||
default byte[] generateSecureLevelKey() {
|
||||
return SecurityHelper.randomBytes(128);
|
||||
}
|
||||
|
||||
default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signature) throws InvalidKeySpecException, SignatureException {
|
||||
if(publicKey == null || signature == null) throw new InvalidKeySpecException();
|
||||
if (publicKey == null || signature == null) throw new InvalidKeySpecException();
|
||||
ECPublicKey pubKey = SecurityHelper.toPublicECKey(publicKey);
|
||||
Signature sign = SecurityHelper.newECVerifySignature(pubKey);
|
||||
sign.update(data);
|
||||
sign.verify(signature);
|
||||
}
|
||||
|
||||
GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event);
|
||||
|
||||
boolean allowGetSecureLevelInfo(Client client);
|
||||
default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client)
|
||||
{
|
||||
|
||||
default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client) {
|
||||
return new SecurityReportRequestEvent();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,6 @@ public abstract class AuthProvider implements AutoCloseable {
|
|||
private static boolean registredProv = false;
|
||||
protected transient LaunchServer srv = null;
|
||||
|
||||
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthType() {
|
||||
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.PASSWORD;
|
||||
}
|
||||
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
|
||||
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
|
||||
}
|
||||
|
||||
public static AuthProviderResult authError(String message) throws AuthException {
|
||||
throw new AuthException(message);
|
||||
}
|
||||
|
@ -38,6 +31,13 @@ public static void registerProviders() {
|
|||
}
|
||||
}
|
||||
|
||||
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthType() {
|
||||
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.PASSWORD;
|
||||
}
|
||||
|
||||
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
|
||||
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the username and password
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import pro.gravit.launcher.ClientPermissions;
|
||||
import pro.gravit.launcher.HTTPRequest;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||
import pro.gravit.launchserver.auth.AuthException;
|
||||
import pro.gravit.launcher.HTTPRequest;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -17,33 +17,6 @@ public final class JsonAuthProvider extends AuthProvider {
|
|||
private URL url;
|
||||
private String apiKey;
|
||||
|
||||
public static class authResult {
|
||||
String username;
|
||||
String error;
|
||||
long permissions;
|
||||
long flags;
|
||||
}
|
||||
|
||||
public static class authRequest {
|
||||
public authRequest(String username, String password, String ip) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public authRequest(String username, String password, String ip, String apiKey) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.ip = ip;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
final String username;
|
||||
final String password;
|
||||
final String ip;
|
||||
String apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws IOException {
|
||||
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
|
||||
|
@ -66,4 +39,31 @@ else if (result.error != null)
|
|||
public void close() {
|
||||
// pass
|
||||
}
|
||||
|
||||
public static class authResult {
|
||||
String username;
|
||||
String error;
|
||||
long permissions;
|
||||
long flags;
|
||||
}
|
||||
|
||||
public static class authRequest {
|
||||
final String username;
|
||||
final String password;
|
||||
final String ip;
|
||||
String apiKey;
|
||||
|
||||
public authRequest(String username, String password, String ip) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public authRequest(String username, String password, String ip, String apiKey) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.ip = ip;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
import java.util.Map;
|
||||
|
||||
public final class RejectAuthProvider extends AuthProvider implements Reconfigurable {
|
||||
private String message;
|
||||
private ArrayList<String> whitelist = new ArrayList<>();
|
||||
|
||||
public RejectAuthProvider() {
|
||||
}
|
||||
|
||||
|
@ -21,9 +24,6 @@ public RejectAuthProvider(String message) {
|
|||
this.message = message;
|
||||
}
|
||||
|
||||
private String message;
|
||||
private ArrayList<String> whitelist = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws AuthException {
|
||||
if (whitelist != null) {
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
import java.util.UUID;
|
||||
|
||||
public final class RequestTextureProvider extends TextureProvider {
|
||||
// Instance
|
||||
private String skinURL;
|
||||
private String cloakURL;
|
||||
|
||||
public RequestTextureProvider() {
|
||||
}
|
||||
|
||||
|
@ -37,11 +41,6 @@ private static String getTextureURL(String url, UUID uuid, String username, Stri
|
|||
"client", IOHelper.urlEncode(client == null ? "unknown" : client));
|
||||
}
|
||||
|
||||
// Instance
|
||||
private String skinURL;
|
||||
|
||||
private String cloakURL;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Do nothing
|
||||
|
|
|
@ -55,7 +55,7 @@ public void replacePre(Predicate<LauncherBuildTask> pred, LauncherBuildTask task
|
|||
}
|
||||
|
||||
public void replace(Predicate<LauncherBuildTask> pred, LauncherBuildTask taskRep) {
|
||||
replaceCounted( 0, pred, taskRep);
|
||||
replaceCounted(0, pred, taskRep);
|
||||
}
|
||||
|
||||
public void replaceAfter(Predicate<LauncherBuildTask> pred, LauncherBuildTask taskRep) {
|
||||
|
|
|
@ -37,6 +37,115 @@ public class BuildContext {
|
|||
public final HashSet<String> fileList;
|
||||
public final HashSet<String> clientModules;
|
||||
|
||||
public BuildContext(ZipOutputStream output, List<JarFile> readerClassPath, MainBuildTask task) {
|
||||
this.output = output;
|
||||
this.readerClassPath = readerClassPath;
|
||||
this.task = task;
|
||||
fileList = new HashSet<>(1024);
|
||||
clientModules = new HashSet<>();
|
||||
}
|
||||
|
||||
public void pushFile(String filename, InputStream inputStream) throws IOException {
|
||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||
output.putNextEntry(zip);
|
||||
IOHelper.transfer(inputStream, output);
|
||||
output.closeEntry();
|
||||
fileList.add(filename);
|
||||
}
|
||||
|
||||
public void pushFile(String filename, StreamObject object) throws IOException {
|
||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||
output.putNextEntry(zip);
|
||||
object.write(new HOutput(output));
|
||||
output.closeEntry();
|
||||
fileList.add(filename);
|
||||
}
|
||||
|
||||
public void pushFile(String filename, Object object, Type type) throws IOException {
|
||||
String bytes = Launcher.gsonManager.gson.toJson(object, type);
|
||||
pushBytes(filename, bytes.getBytes(UNICODE_CHARSET));
|
||||
}
|
||||
|
||||
public void pushDir(Path dir, String targetDir, Map<String, byte[]> hashMap, boolean hidden) throws IOException {
|
||||
IOHelper.walk(dir, new RuntimeDirVisitor(output, hashMap, dir, targetDir), hidden);
|
||||
}
|
||||
|
||||
public void pushBytes(String filename, byte[] bytes) throws IOException {
|
||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||
output.putNextEntry(zip);
|
||||
output.write(bytes);
|
||||
output.closeEntry();
|
||||
fileList.add(filename);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void pushJarFile(ZipInputStream input) throws IOException {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while (e != null) {
|
||||
if (fileList.contains(e.getName())) {
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
IOHelper.transfer(input, output);
|
||||
fileList.add(e.getName());
|
||||
e = input.getNextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void pushJarFile(ZipInputStream input, Set<String> blacklist) throws IOException {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while (e != null) {
|
||||
if (fileList.contains(e.getName()) || blacklist.contains(e.getName())) {
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
IOHelper.transfer(input, output);
|
||||
fileList.add(e.getName());
|
||||
e = input.getNextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
public void pushJarFile(Path jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException {
|
||||
pushJarFile(jarfile.toUri().toURL(), filter, needTransform);
|
||||
}
|
||||
|
||||
public void pushJarFile(URL jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException {
|
||||
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(jarfile))) {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while (e != null) {
|
||||
String filename = e.getName();
|
||||
if (e.isDirectory() || fileList.contains(filename) || filter.test(e)) {
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
} catch (ZipException ex) {
|
||||
LogHelper.warning("Write %s failed: %s", filename, ex.getMessage() == null ? "null" : ex.getMessage());
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
if (filename.endsWith(".class")) {
|
||||
String classname = filename.replace('/', '.').substring(0,
|
||||
filename.length() - ".class".length());
|
||||
if (!needTransform.test(classname)) {
|
||||
IOHelper.transfer(input, output);
|
||||
} else {
|
||||
byte[] bytes = IOHelper.read(input);
|
||||
bytes = task.transformClass(bytes, classname, this);
|
||||
output.write(bytes);
|
||||
}
|
||||
} else
|
||||
IOHelper.transfer(input, output);
|
||||
fileList.add(filename);
|
||||
e = input.getNextEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
|
||||
private final ZipOutputStream output;
|
||||
private final Map<String, byte[]> hashs;
|
||||
|
@ -60,7 +169,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
|
|||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String fileName = IOHelper.toString(sourceDir.relativize(file));
|
||||
if(hashs != null)
|
||||
if (hashs != null)
|
||||
hashs.put(fileName, SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, file));
|
||||
|
||||
// Create zip entry and transfer contents
|
||||
|
@ -72,122 +181,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
|||
}
|
||||
|
||||
private ZipEntry newEntry(String fileName) {
|
||||
return newZipEntry( targetDir + IOHelper.CROSS_SEPARATOR + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BuildContext(ZipOutputStream output, List<JarFile> readerClassPath, MainBuildTask task) {
|
||||
this.output = output;
|
||||
this.readerClassPath = readerClassPath;
|
||||
this.task = task;
|
||||
fileList = new HashSet<>(1024);
|
||||
clientModules = new HashSet<>();
|
||||
}
|
||||
|
||||
public void pushFile(String filename, InputStream inputStream) throws IOException {
|
||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||
output.putNextEntry(zip);
|
||||
IOHelper.transfer(inputStream, output);
|
||||
output.closeEntry();
|
||||
fileList.add(filename);
|
||||
}
|
||||
|
||||
public void pushFile(String filename, StreamObject object) throws IOException
|
||||
{
|
||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||
output.putNextEntry(zip);
|
||||
object.write(new HOutput(output));
|
||||
output.closeEntry();
|
||||
fileList.add(filename);
|
||||
}
|
||||
public void pushFile(String filename, Object object, Type type) throws IOException
|
||||
{
|
||||
String bytes = Launcher.gsonManager.gson.toJson(object, type);
|
||||
pushBytes(filename, bytes.getBytes(UNICODE_CHARSET));
|
||||
}
|
||||
|
||||
public void pushDir(Path dir, String targetDir, Map<String, byte[]> hashMap, boolean hidden) throws IOException
|
||||
{
|
||||
IOHelper.walk(dir, new RuntimeDirVisitor(output, hashMap, dir, targetDir), hidden);
|
||||
}
|
||||
|
||||
public void pushBytes(String filename, byte[] bytes) throws IOException {
|
||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||
output.putNextEntry(zip);
|
||||
output.write(bytes);
|
||||
output.closeEntry();
|
||||
fileList.add(filename);
|
||||
}
|
||||
@Deprecated
|
||||
public void pushJarFile(ZipInputStream input) throws IOException {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while (e != null) {
|
||||
if (fileList.contains(e.getName())) {
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
IOHelper.transfer(input, output);
|
||||
fileList.add(e.getName());
|
||||
e = input.getNextEntry();
|
||||
}
|
||||
}
|
||||
@Deprecated
|
||||
public void pushJarFile(ZipInputStream input, Set<String> blacklist) throws IOException {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while (e != null) {
|
||||
if (fileList.contains(e.getName()) || blacklist.contains(e.getName())) {
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
IOHelper.transfer(input, output);
|
||||
fileList.add(e.getName());
|
||||
e = input.getNextEntry();
|
||||
}
|
||||
}
|
||||
public void pushJarFile(Path jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException
|
||||
{
|
||||
pushJarFile(jarfile.toUri().toURL(), filter, needTransform);
|
||||
}
|
||||
public void pushJarFile(URL jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException
|
||||
{
|
||||
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(jarfile))) {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while(e != null)
|
||||
{
|
||||
String filename = e.getName();
|
||||
if(e.isDirectory() || fileList.contains(filename) || filter.test(e))
|
||||
{
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
} catch (ZipException ex) {
|
||||
LogHelper.warning("Write %s failed: %s", filename, ex.getMessage() == null ? "null" : ex.getMessage());
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
if (filename.endsWith(".class")) {
|
||||
String classname = filename.replace('/', '.').substring(0,
|
||||
filename.length() - ".class".length());
|
||||
if(!needTransform.test(classname))
|
||||
{
|
||||
IOHelper.transfer(input, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] bytes = IOHelper.read(input);
|
||||
bytes = task.transformClass(bytes, classname, this);
|
||||
output.write(bytes);
|
||||
}
|
||||
} else
|
||||
IOHelper.transfer(input, output);
|
||||
fileList.add(filename);
|
||||
e = input.getNextEntry();
|
||||
}
|
||||
return newZipEntry(targetDir + IOHelper.CROSS_SEPARATOR + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public JARLauncherBinary(LaunchServer server) throws IOException {
|
|||
@Override
|
||||
public void init() {
|
||||
tasks.add(new PrepareBuildTask(server));
|
||||
if(!server.config.sign.enabled) tasks.add(new CertificateAutogenTask(server));
|
||||
if (!server.config.sign.enabled) tasks.add(new CertificateAutogenTask(server));
|
||||
tasks.add(new MainBuildTask(server));
|
||||
if (server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
|
||||
tasks.add(new ProGuardBuildTask(server));
|
||||
|
|
|
@ -19,17 +19,18 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat
|
|||
syncBinaryFile = binaryFile;
|
||||
}
|
||||
|
||||
public void build() throws IOException
|
||||
{
|
||||
build(syncBinaryFile, server.config.launcher.deleteTempFiles);
|
||||
public static Path resolve(LaunchServer server, String ext) {
|
||||
return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
|
||||
}
|
||||
|
||||
public void build() throws IOException {
|
||||
build(syncBinaryFile, server.config.launcher.deleteTempFiles);
|
||||
}
|
||||
|
||||
public final boolean exists() {
|
||||
return syncBinaryFile != null && IOHelper.isFile(syncBinaryFile);
|
||||
}
|
||||
|
||||
|
||||
public final byte[] getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
@ -48,8 +49,4 @@ public final boolean sync() throws IOException {
|
|||
|
||||
return exists;
|
||||
}
|
||||
|
||||
public static Path resolve(LaunchServer server, String ext) {
|
||||
return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,27 +14,16 @@
|
|||
import java.util.List;
|
||||
|
||||
public class ProguardConf {
|
||||
public static final String[] JAVA9_OPTS = new String[]{
|
||||
"-libraryjars '<java.home>/jmods/'"
|
||||
};
|
||||
public static final String[] JAVA8_OPTS = new String[]{
|
||||
"-libraryjars '<java.home>/lib/rt.jar'",
|
||||
"-libraryjars '<java.home>/lib/jce.jar'",
|
||||
"-libraryjars '<java.home>/lib/ext/nashorn.jar'",
|
||||
"-libraryjars '<java.home>/lib/ext/jfxrt.jar'"
|
||||
};
|
||||
private static final char[] chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ".toCharArray();
|
||||
|
||||
public static final String[] JAVA9_OPTS = new String[] {
|
||||
"-libraryjars '<java.home>/jmods/'"
|
||||
};
|
||||
public static final String[] JAVA8_OPTS = new String[] {
|
||||
"-libraryjars '<java.home>/lib/rt.jar'",
|
||||
"-libraryjars '<java.home>/lib/jce.jar'",
|
||||
"-libraryjars '<java.home>/lib/ext/nashorn.jar'",
|
||||
"-libraryjars '<java.home>/lib/ext/jfxrt.jar'"
|
||||
};
|
||||
private static String generateString(SecureRandom rand, String lowString, String upString, int il) {
|
||||
StringBuilder sb = new StringBuilder(Math.max(il, lowString.length()));
|
||||
for (int i = 0; i < lowString.length(); ++i) {
|
||||
sb.append(rand.nextBoolean() ? lowString.charAt(i) : upString.charAt(i));
|
||||
}
|
||||
int toI = il - lowString.length();
|
||||
for (int i = 0; i < toI; i++) sb.append(chars[rand.nextInt(chars.length)]);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public final Path proguard;
|
||||
public final Path config;
|
||||
public final Path mappings;
|
||||
|
@ -49,6 +38,16 @@ public ProguardConf(LaunchServer srv) {
|
|||
this.srv = srv;
|
||||
}
|
||||
|
||||
private static String generateString(SecureRandom rand, String lowString, String upString, int il) {
|
||||
StringBuilder sb = new StringBuilder(Math.max(il, lowString.length()));
|
||||
for (int i = 0; i < lowString.length(); ++i) {
|
||||
sb.append(rand.nextBoolean() ? lowString.charAt(i) : upString.charAt(i));
|
||||
}
|
||||
int toI = il - lowString.length();
|
||||
for (int i = 0; i < toI; i++) sb.append(chars[rand.nextInt(chars.length)]);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String[] buildConfig(Path inputJar, Path outputJar) {
|
||||
List<String> confStrs = new ArrayList<>();
|
||||
prepare(false);
|
||||
|
@ -57,11 +56,11 @@ public String[] buildConfig(Path inputJar, Path outputJar) {
|
|||
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
|
||||
confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'");
|
||||
confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'");
|
||||
Collections.addAll(confStrs, JVMHelper.JVM_VERSION >= 9 ? JAVA9_OPTS : JAVA8_OPTS);
|
||||
Collections.addAll(confStrs, JVMHelper.JVM_VERSION >= 9 ? JAVA9_OPTS : JAVA8_OPTS);
|
||||
srv.launcherBinary.coreLibs.stream()
|
||||
.map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'")
|
||||
.forEach(confStrs::add);
|
||||
|
||||
|
||||
srv.launcherBinary.addonLibs.stream()
|
||||
.map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'")
|
||||
.forEach(confStrs::add);
|
||||
|
|
|
@ -40,22 +40,19 @@
|
|||
*/
|
||||
public class SignerJar implements AutoCloseable {
|
||||
|
||||
private static final String MANIFEST_FN = "META-INF/MANIFEST.MF";
|
||||
private static final String MANIFEST_FN = "META-INF/MANIFEST.MF";
|
||||
private static final String DIGEST_HASH = SignHelper.hashFunctionName + "-Digest";
|
||||
private final String SIG_FN;
|
||||
private final String SIG_KEY_FN;
|
||||
private static final String DIGEST_HASH = SignHelper.hashFunctionName + "-Digest";
|
||||
|
||||
private final ZipOutputStream zos;
|
||||
|
||||
private final Map<String, String> manifestAttributes;
|
||||
private final Map<String, String> fileDigests;
|
||||
private final Map<String, String> sectionDigests;
|
||||
private final Supplier<CMSSignedDataGenerator> gen;
|
||||
private String manifestHash;
|
||||
private String manifestMainHash;
|
||||
|
||||
private final Map<String, String> fileDigests;
|
||||
|
||||
private final Map<String, String> sectionDigests;
|
||||
private final Supplier<CMSSignedDataGenerator> gen;
|
||||
|
||||
public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, String sig_fn, String sig_key_fn) {
|
||||
zos = out;
|
||||
this.gen = gen;
|
||||
|
@ -76,7 +73,7 @@ public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, Stri
|
|||
* @throws NullPointerException if any of the arguments is {@code null}
|
||||
*/
|
||||
public void addFileContents(String filename, byte[] contents) throws IOException {
|
||||
addFileContents(filename, new ByteArrayInputStream(contents));
|
||||
addFileContents(filename, new ByteArrayInputStream(contents));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +86,7 @@ public void addFileContents(String filename, byte[] contents) throws IOException
|
|||
* @throws NullPointerException if any of the arguments is {@code null}
|
||||
*/
|
||||
public void addFileContents(String filename, InputStream contents) throws IOException {
|
||||
addFileContents(IOHelper.newZipEntry(filename), contents);
|
||||
addFileContents(IOHelper.newZipEntry(filename), contents);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,7 +99,7 @@ public void addFileContents(String filename, InputStream contents) throws IOExce
|
|||
* @throws NullPointerException if any of the arguments is {@code null}
|
||||
*/
|
||||
public void addFileContents(ZipEntry entry, byte[] contents) throws IOException {
|
||||
addFileContents(entry, new ByteArrayInputStream(contents));
|
||||
addFileContents(entry, new ByteArrayInputStream(contents));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,7 +135,7 @@ public void addManifestAttribute(String name, String value) {
|
|||
* underlying stream.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RuntimeException if the signing goes wrong
|
||||
* @throws RuntimeException if the signing goes wrong
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
@ -152,7 +149,7 @@ public void close() throws IOException {
|
|||
* underlying stream open.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RuntimeException if the signing goes wrong
|
||||
* @throws RuntimeException if the signing goes wrong
|
||||
*/
|
||||
public void finish() throws IOException {
|
||||
writeManifest();
|
||||
|
@ -272,7 +269,7 @@ private byte[] writeSigFile() throws IOException {
|
|||
* Signs the .SIG file and writes the signature (.RSA file) to the JAR.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RuntimeException if the signing failed
|
||||
* @throws RuntimeException if the signing failed
|
||||
*/
|
||||
private void writeSignature(byte[] sigFile) throws IOException {
|
||||
zos.putNextEntry(IOHelper.newZipEntry(SIG_KEY_FN));
|
||||
|
|
|
@ -24,20 +24,6 @@ public AdditionalFixesApplyTask(LaunchServer server) {
|
|||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "AdditionalFixesApply";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path process(Path inputFile) throws IOException {
|
||||
Path out = server.launcherBinary.nextPath("post-fixed");
|
||||
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(out))) {
|
||||
apply(inputFile, inputFile, output, server, (e) -> false, true);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static void apply(Path inputFile, Path addFile, ZipOutputStream output, LaunchServer srv, Predicate<ZipEntry> excluder, boolean needFixes) throws IOException {
|
||||
try (ClassMetadataReader reader = new ClassMetadataReader()) {
|
||||
reader.getCp().add(new JarFile(inputFile.toFile()));
|
||||
|
@ -52,11 +38,10 @@ public static void apply(Path inputFile, Path addFile, ZipOutputStream output, L
|
|||
output.putNextEntry(IOHelper.newZipEntry(e));
|
||||
if (filename.endsWith(".class")) {
|
||||
byte[] bytes;
|
||||
if(needFixes) {
|
||||
if (needFixes) {
|
||||
bytes = classFix(input, reader, srv.config.launcher.stripLineNumbers);
|
||||
output.write(bytes);
|
||||
}
|
||||
else
|
||||
} else
|
||||
IOHelper.transfer(input, output);
|
||||
} else
|
||||
IOHelper.transfer(input, output);
|
||||
|
@ -75,6 +60,20 @@ private static byte[] classFix(InputStream input, ClassMetadataReader reader, bo
|
|||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "AdditionalFixesApply";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path process(Path inputFile) throws IOException {
|
||||
Path out = server.launcherBinary.nextPath("post-fixed");
|
||||
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(out))) {
|
||||
apply(inputFile, inputFile, output, server, (e) -> false, true);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowDelete() {
|
||||
return true;
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
import org.bouncycastle.asn1.x509.*;
|
||||
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.KeyPurposeId;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
|
@ -29,6 +32,9 @@
|
|||
import java.util.Date;
|
||||
|
||||
public class CertificateAutogenTask implements LauncherBuildTask {
|
||||
public X509Certificate certificate;
|
||||
public X509CertificateHolder bcCertificate;
|
||||
public CMSSignedDataGenerator signedDataGenerator;
|
||||
private LaunchServer server;
|
||||
|
||||
public CertificateAutogenTask(LaunchServer server) {
|
||||
|
@ -39,13 +45,10 @@ public CertificateAutogenTask(LaunchServer server) {
|
|||
public String getName() {
|
||||
return "CertificateAutogen";
|
||||
}
|
||||
public X509Certificate certificate;
|
||||
public X509CertificateHolder bcCertificate;
|
||||
public CMSSignedDataGenerator signedDataGenerator;
|
||||
|
||||
@Override
|
||||
public Path process(Path inputFile) throws IOException {
|
||||
if(signedDataGenerator != null) return inputFile;
|
||||
if (signedDataGenerator != null) return inputFile;
|
||||
try {
|
||||
X500NameBuilder subject = new X500NameBuilder();
|
||||
subject.addRDN(BCStyle.CN, server.config.projectName.concat(" Autogenerated"));
|
||||
|
@ -63,8 +66,8 @@ public Path process(Path inputFile) throws IOException {
|
|||
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256WITHECDSA");
|
||||
ContentSigner signer = csBuilder.build(server.privateKey);
|
||||
bcCertificate = builder.build(signer);
|
||||
certificate = new JcaX509CertificateConverter().setProvider( "BC" )
|
||||
.getCertificate( bcCertificate );
|
||||
certificate = new JcaX509CertificateConverter().setProvider("BC")
|
||||
.getCertificate(bcCertificate);
|
||||
ArrayList<Certificate> chain = new ArrayList<>();
|
||||
chain.add(certificate);
|
||||
signedDataGenerator = SignHelper.createSignedDataGenerator(server.privateKey, certificate, chain, "SHA256WITHECDSA");
|
||||
|
|
|
@ -27,94 +27,12 @@
|
|||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class MainBuildTask implements LauncherBuildTask {
|
||||
private final LaunchServer server;
|
||||
public final ClassMetadataReader reader;
|
||||
@FunctionalInterface
|
||||
public interface Transformer {
|
||||
byte[] transform(byte[] input, String classname, BuildContext context);
|
||||
}
|
||||
public static class IOHookSet<R> {
|
||||
public final Set<IOHook<R>> list = new HashSet<>();
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IOHook<R> {
|
||||
/**
|
||||
* @param context custom param
|
||||
* False to continue processing hook
|
||||
* @throws HookException The hook may return the error text throwing this exception
|
||||
*/
|
||||
void hook(R context) throws HookException, IOException;
|
||||
}
|
||||
|
||||
public void registerHook(IOHook<R> hook) {
|
||||
list.add(hook);
|
||||
}
|
||||
|
||||
public boolean unregisterHook(IOHook<R> hook) {
|
||||
return list.remove(hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context custom param
|
||||
* False to continue
|
||||
* @throws HookException The hook may return the error text throwing this exception
|
||||
*/
|
||||
public void hook(R context) throws HookException, IOException {
|
||||
for (IOHook<R> hook : list) {
|
||||
hook.hook(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ASMTransformer extends Transformer {
|
||||
default byte[] transform(byte[] input, String classname, BuildContext context)
|
||||
{
|
||||
ClassReader reader = new ClassReader(input);
|
||||
ClassNode cn = new ClassNode();
|
||||
reader.accept(cn, 0);
|
||||
transform(cn, classname, context);
|
||||
SafeClassWriter writer = new SafeClassWriter(context.task.reader,ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(writer);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
void transform(ClassNode cn, String classname, BuildContext context);
|
||||
}
|
||||
public abstract static class ASMAnnotationFieldProcessor implements ASMTransformer
|
||||
{
|
||||
private final String desc;
|
||||
|
||||
protected ASMAnnotationFieldProcessor(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(ClassNode cn, String classname, BuildContext context) {
|
||||
for(FieldNode fn : cn.fields)
|
||||
{
|
||||
if(fn.invisibleAnnotations == null || fn.invisibleAnnotations.isEmpty()) continue;
|
||||
AnnotationNode found = null;
|
||||
for(AnnotationNode an : fn.invisibleAnnotations)
|
||||
{
|
||||
if(an == null) continue;
|
||||
if(desc.equals(an.desc))
|
||||
{
|
||||
found = an;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found != null)
|
||||
{
|
||||
transformField(found, fn, cn, classname, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
abstract public void transformField(AnnotationNode an, FieldNode fn, ClassNode cn, String classname, BuildContext context);
|
||||
}
|
||||
private final LaunchServer server;
|
||||
public Set<String> blacklist = new HashSet<>();
|
||||
public List<Transformer> transformers = new ArrayList<>();
|
||||
public IOHookSet<BuildContext> preBuildHook = new IOHookSet<>();
|
||||
public IOHookSet<BuildContext> postBuildHook = new IOHookSet<>();
|
||||
|
||||
public Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
public MainBuildTask(LaunchServer srv) {
|
||||
|
@ -163,7 +81,7 @@ public Path process(Path inputJar) throws IOException {
|
|||
}
|
||||
|
||||
protected void postInitProps() {
|
||||
List<byte[]> certificates = Arrays.stream(server.certificateManager.trustManager.getTrusted()).map(e -> {
|
||||
List<byte[]> certificates = Arrays.stream(server.certificateManager.trustManager.getTrusted()).map(e -> {
|
||||
try {
|
||||
return e.getEncoded();
|
||||
} catch (CertificateEncodingException e2) {
|
||||
|
@ -171,8 +89,7 @@ protected void postInitProps() {
|
|||
return new byte[0];
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
if(!server.config.sign.enabled)
|
||||
{
|
||||
if (!server.config.sign.enabled) {
|
||||
CertificateAutogenTask task = server.launcherBinary.getTaskByClass(CertificateAutogenTask.class).get();
|
||||
try {
|
||||
certificates.add(task.certificate.getEncoded());
|
||||
|
@ -181,10 +98,10 @@ protected void postInitProps() {
|
|||
}
|
||||
}
|
||||
properties.put("launchercore.certificates", certificates);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initProps() {
|
||||
properties.clear();
|
||||
protected void initProps() {
|
||||
properties.clear();
|
||||
properties.put("launcher.address", server.config.netty.address);
|
||||
properties.put("launcher.projectName", server.config.projectName);
|
||||
properties.put("runtimeconfig.secretKeyClient", SecurityHelper.randomStringAESKey());
|
||||
|
@ -201,46 +118,38 @@ protected void initProps() {
|
|||
//LogHelper.debug("[checkSecure] %s: %s", launcherSalt, Arrays.toString(launcherSecureHash));
|
||||
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
|
||||
properties.put("runtimeconfig.oemUnlockKey", server.runtime.oemUnlockKey);
|
||||
|
||||
}
|
||||
|
||||
public byte[] transformClass(byte[] bytes, String classname, BuildContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public byte[] transformClass(byte[] bytes, String classname, BuildContext context) {
|
||||
byte[] result = bytes;
|
||||
ClassReader cr = null;
|
||||
ClassWriter writer = null;
|
||||
ClassNode cn = null;
|
||||
for(Transformer t : transformers)
|
||||
{
|
||||
if(t instanceof ASMTransformer)
|
||||
{
|
||||
for (Transformer t : transformers) {
|
||||
if (t instanceof ASMTransformer) {
|
||||
ASMTransformer asmTransformer = (ASMTransformer) t;
|
||||
if(cn == null)
|
||||
{
|
||||
if (cn == null) {
|
||||
cr = new ClassReader(result);
|
||||
cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
}
|
||||
asmTransformer.transform(cn, classname, context);
|
||||
continue;
|
||||
}
|
||||
else if(cn != null)
|
||||
{
|
||||
} else if (cn != null) {
|
||||
writer = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(writer);
|
||||
result = writer.toByteArray();
|
||||
}
|
||||
byte[] old_result = result;
|
||||
result = t.transform(result, classname, context);
|
||||
if(old_result != result)
|
||||
{
|
||||
if (old_result != result) {
|
||||
cr = null;
|
||||
cn = null;
|
||||
}
|
||||
}
|
||||
if(cn != null)
|
||||
{
|
||||
writer = new SafeClassWriter(reader,ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
if (cn != null) {
|
||||
writer = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(writer);
|
||||
result = writer.toByteArray();
|
||||
}
|
||||
|
@ -251,4 +160,84 @@ else if(cn != null)
|
|||
public boolean allowDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Transformer {
|
||||
byte[] transform(byte[] input, String classname, BuildContext context);
|
||||
}
|
||||
|
||||
public interface ASMTransformer extends Transformer {
|
||||
default byte[] transform(byte[] input, String classname, BuildContext context) {
|
||||
ClassReader reader = new ClassReader(input);
|
||||
ClassNode cn = new ClassNode();
|
||||
reader.accept(cn, 0);
|
||||
transform(cn, classname, context);
|
||||
SafeClassWriter writer = new SafeClassWriter(context.task.reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(writer);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
void transform(ClassNode cn, String classname, BuildContext context);
|
||||
}
|
||||
|
||||
public static class IOHookSet<R> {
|
||||
public final Set<IOHook<R>> list = new HashSet<>();
|
||||
|
||||
public void registerHook(IOHook<R> hook) {
|
||||
list.add(hook);
|
||||
}
|
||||
|
||||
public boolean unregisterHook(IOHook<R> hook) {
|
||||
return list.remove(hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context custom param
|
||||
* False to continue
|
||||
* @throws HookException The hook may return the error text throwing this exception
|
||||
*/
|
||||
public void hook(R context) throws HookException, IOException {
|
||||
for (IOHook<R> hook : list) {
|
||||
hook.hook(context);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IOHook<R> {
|
||||
/**
|
||||
* @param context custom param
|
||||
* False to continue processing hook
|
||||
* @throws HookException The hook may return the error text throwing this exception
|
||||
*/
|
||||
void hook(R context) throws HookException, IOException;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class ASMAnnotationFieldProcessor implements ASMTransformer {
|
||||
private final String desc;
|
||||
|
||||
protected ASMAnnotationFieldProcessor(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(ClassNode cn, String classname, BuildContext context) {
|
||||
for (FieldNode fn : cn.fields) {
|
||||
if (fn.invisibleAnnotations == null || fn.invisibleAnnotations.isEmpty()) continue;
|
||||
AnnotationNode found = null;
|
||||
for (AnnotationNode an : fn.invisibleAnnotations) {
|
||||
if (an == null) continue;
|
||||
if (desc.equals(an.desc)) {
|
||||
found = an;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
transformField(found, fn, cn, classname, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract public void transformField(AnnotationNode an, FieldNode fn, ClassNode cn, String classname, BuildContext context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,17 @@ public SignJarTask(LaunchServerConfig.JarSignerConf config, LaunchServer srv) {
|
|||
this.srv = srv;
|
||||
}
|
||||
|
||||
public static CMSSignedDataGenerator gen(LaunchServerConfig.JarSignerConf config, KeyStore c) {
|
||||
try {
|
||||
return SignHelper.createSignedDataGenerator(c,
|
||||
config.keyAlias, config.signAlgo, config.keyPass);
|
||||
} catch (CertificateEncodingException | UnrecoverableKeyException | KeyStoreException
|
||||
| OperatorCreationException | NoSuchAlgorithmException | CMSException e) {
|
||||
LogHelper.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "SignJar";
|
||||
|
@ -46,7 +57,7 @@ public Path process(Path inputFile) throws IOException {
|
|||
}
|
||||
|
||||
public void sign(LaunchServerConfig.JarSignerConf config, Path inputFile, Path signedFile) throws IOException {
|
||||
if(config.enabled) stdSign(config, inputFile, signedFile);
|
||||
if (config.enabled) stdSign(config, inputFile, signedFile);
|
||||
else autoSign(inputFile, signedFile);
|
||||
}
|
||||
|
||||
|
@ -69,6 +80,7 @@ private void stdSign(LaunchServerConfig.JarSignerConf config, Path inputFile, Pa
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void autoSign(Path inputFile, Path signedFile) throws IOException {
|
||||
try (SignerJar output = new SignerJar(new ZipOutputStream(IOHelper.newOutput(signedFile)), () -> {
|
||||
CertificateAutogenTask task = srv.launcherBinary.getTaskByClass(CertificateAutogenTask.class).get();
|
||||
|
@ -95,15 +107,4 @@ private void autoSign(Path inputFile, Path signedFile) throws IOException {
|
|||
public boolean allowDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static CMSSignedDataGenerator gen(LaunchServerConfig.JarSignerConf config, KeyStore c) {
|
||||
try {
|
||||
return SignHelper.createSignedDataGenerator(c,
|
||||
config.keyAlias, config.signAlgo, config.keyPass);
|
||||
} catch (CertificateEncodingException | UnrecoverableKeyException | KeyStoreException
|
||||
| OperatorCreationException | NoSuchAlgorithmException | CMSException e) {
|
||||
LogHelper.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,23 +13,10 @@
|
|||
import java.nio.file.Path;
|
||||
|
||||
public class Launch4JTask implements LauncherBuildTask {
|
||||
private final static class Launch4JLog extends Log {
|
||||
private static final Launch4JLog INSTANCE = new Launch4JLog();
|
||||
|
||||
@Override
|
||||
public void append(String s) {
|
||||
LogHelper.subInfo(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
|
||||
private static final String VERSION = Version.getVersion().getVersionString();
|
||||
private static final int BUILD = Version.getVersion().build;
|
||||
private final Path faviconFile;
|
||||
|
||||
private final LaunchServer server;
|
||||
|
||||
public Launch4JTask(LaunchServer launchServer) {
|
||||
|
@ -37,6 +24,10 @@ public Launch4JTask(LaunchServer launchServer) {
|
|||
faviconFile = launchServer.dir.resolve("favicon.ico");
|
||||
}
|
||||
|
||||
public static String formatVars(String mask) {
|
||||
return String.format(mask, VERSION, BUILD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "launch4j";
|
||||
|
@ -70,6 +61,7 @@ public Path process(Path inputFile) throws IOException {
|
|||
public boolean allowDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private Path setConfig() {
|
||||
Path path = server.launcherEXEBinary.nextPath(getName());
|
||||
Config config = new Config();
|
||||
|
@ -120,10 +112,17 @@ private Path setConfig() {
|
|||
return path;
|
||||
}
|
||||
|
||||
private static final String VERSION = Version.getVersion().getVersionString();
|
||||
private static final int BUILD = Version.getVersion().build;
|
||||
private final static class Launch4JLog extends Log {
|
||||
private static final Launch4JLog INSTANCE = new Launch4JLog();
|
||||
|
||||
public static String formatVars(String mask) {
|
||||
return String.format(mask, VERSION, BUILD);
|
||||
@Override
|
||||
public void append(String s) {
|
||||
LogHelper.subInfo(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
import java.security.KeyPair;
|
||||
|
||||
public class TestCommand extends Command {
|
||||
private NettyServerSocketHandler handler = null;
|
||||
|
||||
public TestCommand(LaunchServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
private NettyServerSocketHandler handler = null;
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return null;
|
||||
|
|
|
@ -20,65 +20,23 @@
|
|||
import java.util.Collections;
|
||||
|
||||
public final class IndexAssetCommand extends Command {
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
public static class IndexObject {
|
||||
final long size;
|
||||
|
||||
public IndexObject(long size, String hash) {
|
||||
this.size = size;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
final String hash;
|
||||
}
|
||||
|
||||
private static final class IndexAssetVisitor extends SimpleFileVisitor<Path> {
|
||||
private final JsonObject objects;
|
||||
private final Path inputAssetDir;
|
||||
private final Path outputAssetDir;
|
||||
|
||||
private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Path outputAssetDir) {
|
||||
this.objects = objects;
|
||||
this.inputAssetDir = inputAssetDir;
|
||||
this.outputAssetDir = outputAssetDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String name = IOHelper.toString(inputAssetDir.relativize(file));
|
||||
LogHelper.subInfo("Indexing: '%s'", name);
|
||||
|
||||
// Add to index and copy file
|
||||
String digest = SecurityHelper.toHex(SecurityHelper.digest(DigestAlgorithm.SHA1, file));
|
||||
IndexObject obj = new IndexObject(attrs.size(), digest);
|
||||
objects.add(name, gson.toJsonTree(obj));
|
||||
IOHelper.copy(file, resolveObjectFile(outputAssetDir, digest));
|
||||
|
||||
// Continue visiting
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String INDEXES_DIR = "indexes";
|
||||
public static final String OBJECTS_DIR = "objects";
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
private static final String JSON_EXTENSION = ".json";
|
||||
|
||||
public IndexAssetCommand(LaunchServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public static Path resolveIndexFile(Path assetDir, String name) {
|
||||
return assetDir.resolve(INDEXES_DIR).resolve(name + JSON_EXTENSION);
|
||||
}
|
||||
|
||||
|
||||
public static Path resolveObjectFile(Path assetDir, String hash) {
|
||||
return assetDir.resolve(OBJECTS_DIR).resolve(hash.substring(0, 2)).resolve(hash);
|
||||
}
|
||||
|
||||
public IndexAssetCommand(LaunchServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[dir] [index] [output-dir]";
|
||||
|
@ -122,4 +80,41 @@ public void invoke(String... args) throws Exception {
|
|||
server.syncUpdatesDir(Collections.singleton(outputAssetDirName));
|
||||
LogHelper.subInfo("Asset successfully indexed: '%s'", inputAssetDirName);
|
||||
}
|
||||
|
||||
public static class IndexObject {
|
||||
final long size;
|
||||
final String hash;
|
||||
|
||||
public IndexObject(long size, String hash) {
|
||||
this.size = size;
|
||||
this.hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class IndexAssetVisitor extends SimpleFileVisitor<Path> {
|
||||
private final JsonObject objects;
|
||||
private final Path inputAssetDir;
|
||||
private final Path outputAssetDir;
|
||||
|
||||
private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Path outputAssetDir) {
|
||||
this.objects = objects;
|
||||
this.inputAssetDir = inputAssetDir;
|
||||
this.outputAssetDir = outputAssetDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String name = IOHelper.toString(inputAssetDir.relativize(file));
|
||||
LogHelper.subInfo("Indexing: '%s'", name);
|
||||
|
||||
// Add to index and copy file
|
||||
String digest = SecurityHelper.toHex(SecurityHelper.digest(DigestAlgorithm.SHA1, file));
|
||||
IndexObject obj = new IndexObject(attrs.size(), digest);
|
||||
objects.add(name, gson.toJsonTree(obj));
|
||||
IOHelper.copy(file, resolveObjectFile(outputAssetDir, digest));
|
||||
|
||||
// Continue visiting
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,13 @@ public SaveProfilesCommand(LaunchServer server) {
|
|||
super(server);
|
||||
}
|
||||
|
||||
public static void saveProfile(ClientProfile profile, Path path) throws IOException {
|
||||
if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
|
||||
try (Writer w = IOHelper.newWriter(path)) {
|
||||
Launcher.gsonManager.configGson.toJson(profile, w);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[profile names...]";
|
||||
|
@ -32,19 +39,15 @@ public String getUsageDescription() {
|
|||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 1);
|
||||
if(args.length > 0)
|
||||
{
|
||||
for(String profileName : args)
|
||||
{
|
||||
if (args.length > 0) {
|
||||
for (String profileName : args) {
|
||||
Path profilePath = server.profilesDir.resolve(profileName.concat(".json"));
|
||||
if(!Files.exists(profilePath))
|
||||
{
|
||||
if (!Files.exists(profilePath)) {
|
||||
LogHelper.error("Profile %s not found", profilePath.toString());
|
||||
return;
|
||||
}
|
||||
ClientProfile profile;
|
||||
try(Reader reader = IOHelper.newReader(profilePath))
|
||||
{
|
||||
try (Reader reader = IOHelper.newReader(profilePath)) {
|
||||
profile = Launcher.gsonManager.configGson.fromJson(reader, ClientProfile.class);
|
||||
}
|
||||
saveProfile(profile, profilePath);
|
||||
|
@ -53,12 +56,4 @@ public void invoke(String... args) throws Exception {
|
|||
server.syncProfilesDir();
|
||||
}
|
||||
}
|
||||
public static void saveProfile(ClientProfile profile, Path path) throws IOException
|
||||
{
|
||||
if(profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
|
||||
try(Writer w = IOHelper.newWriter(path))
|
||||
{
|
||||
Launcher.gsonManager.configGson.toJson(profile, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,26 +12,6 @@
|
|||
import java.util.Optional;
|
||||
|
||||
public class SignDirCommand extends Command {
|
||||
private class SignJarVisitor extends SimpleFileVisitor<Path>
|
||||
{
|
||||
private SignJarTask task;
|
||||
public SignJarVisitor(SignJarTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.toFile().getName().endsWith(".jar"))
|
||||
{
|
||||
Path tmpSign = server.dir.resolve("build").resolve(file.toFile().getName());
|
||||
LogHelper.info("Signing jar %s", file.toString());
|
||||
task.sign(server.config.sign, file, tmpSign);
|
||||
Files.deleteIfExists(file);
|
||||
Files.move(tmpSign, file);
|
||||
}
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
public SignDirCommand(LaunchServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
@ -50,11 +30,31 @@ public String getUsageDescription() {
|
|||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 1);
|
||||
Path targetDir = Paths.get(args[0]);
|
||||
if(!IOHelper.isDir(targetDir))
|
||||
if (!IOHelper.isDir(targetDir))
|
||||
throw new IllegalArgumentException(String.format("%s not directory", targetDir.toString()));
|
||||
Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class);
|
||||
if(!task.isPresent()) throw new IllegalStateException("SignJarTask not found");
|
||||
if (!task.isPresent()) throw new IllegalStateException("SignJarTask not found");
|
||||
IOHelper.walk(targetDir, new SignJarVisitor(task.get()), true);
|
||||
LogHelper.info("Success signed");
|
||||
}
|
||||
|
||||
private class SignJarVisitor extends SimpleFileVisitor<Path> {
|
||||
private SignJarTask task;
|
||||
|
||||
public SignJarVisitor(SignJarTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.toFile().getName().endsWith(".jar")) {
|
||||
Path tmpSign = server.dir.resolve("build").resolve(file.toFile().getName());
|
||||
LogHelper.info("Signing jar %s", file.toString());
|
||||
task.sign(server.config.sign, file, tmpSign);
|
||||
Files.deleteIfExists(file);
|
||||
Files.move(tmpSign, file);
|
||||
}
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,16 +30,15 @@ public void invoke(String... args) throws Exception {
|
|||
verifyArgs(args, 1);
|
||||
Path target = Paths.get(args[0]);
|
||||
Path tmpSign;
|
||||
if(args.length > 1)
|
||||
if (args.length > 1)
|
||||
tmpSign = Paths.get(args[1]);
|
||||
else
|
||||
tmpSign = server.dir.resolve("build").resolve(target.toFile().getName());
|
||||
LogHelper.info("Signing jar %s to %s", target.toString(), tmpSign.toString());
|
||||
Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class);
|
||||
if(!task.isPresent()) throw new IllegalStateException("SignJarTask not found");
|
||||
if (!task.isPresent()) throw new IllegalStateException("SignJarTask not found");
|
||||
task.get().sign(server.config.sign, target, tmpSign);
|
||||
if(args.length <= 1)
|
||||
{
|
||||
if (args.length <= 1) {
|
||||
LogHelper.info("Move temp jar %s to %s", tmpSign.toString(), target.toString());
|
||||
Files.deleteIfExists(target);
|
||||
Files.move(tmpSign, target);
|
||||
|
|
|
@ -12,9 +12,10 @@
|
|||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractLimiter<T> extends Component implements NeedGarbageCollection, Reconfigurable {
|
||||
public final List<T> exclude = new ArrayList<>();
|
||||
protected final transient Map<T, LimitEntry> map = new HashMap<>();
|
||||
public int rateLimit;
|
||||
public int rateLimitMillis;
|
||||
public final List<T> exclude = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Map<String, Command> getCommands() {
|
||||
|
@ -67,23 +68,6 @@ public void garbageCollection() {
|
|||
map.entrySet().removeIf((e) -> e.getValue().time + rateLimitMillis < time);
|
||||
}
|
||||
|
||||
static class LimitEntry {
|
||||
long time;
|
||||
int trys;
|
||||
|
||||
public LimitEntry(long time, int trys) {
|
||||
this.time = time;
|
||||
this.trys = trys;
|
||||
}
|
||||
|
||||
public LimitEntry() {
|
||||
time = System.currentTimeMillis();
|
||||
trys = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected final transient Map<T, LimitEntry> map = new HashMap<>();
|
||||
|
||||
public boolean check(T address) {
|
||||
if (exclude.contains(address)) return true;
|
||||
LimitEntry entry = map.get(address);
|
||||
|
@ -105,4 +89,19 @@ public boolean check(T address) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class LimitEntry {
|
||||
long time;
|
||||
int trys;
|
||||
|
||||
public LimitEntry(long time, int trys) {
|
||||
this.time = time;
|
||||
this.trys = trys;
|
||||
}
|
||||
|
||||
public LimitEntry() {
|
||||
time = System.currentTimeMillis();
|
||||
trys = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import pro.gravit.utils.HookException;
|
||||
|
||||
public class AuthLimiterComponent extends IPLimiter implements NeedGarbageCollection, AutoCloseable {
|
||||
public String message;
|
||||
private transient LaunchServer srv;
|
||||
|
||||
@Override
|
||||
|
@ -31,8 +32,6 @@ public boolean preAuthHook(AuthResponse.AuthContext context, Client client) {
|
|||
return false;
|
||||
}
|
||||
|
||||
public String message;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
srv.authHookManager.preHook.unregisterHook(this::preAuthHook);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.Reconfigurable;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.handler.MemoryAuthHandler;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
|
@ -22,43 +21,109 @@
|
|||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class LaunchServerConfig {
|
||||
public String projectName;
|
||||
public String[] mirrors;
|
||||
public String binaryName;
|
||||
public boolean copyBinaries = true;
|
||||
public LauncherConfig.LauncherEnvironment env;
|
||||
public Map<String, AuthProviderPair> auth;
|
||||
public DaoProvider dao;
|
||||
|
||||
// Handlers & Providers
|
||||
public ProtectHandler protectHandler;
|
||||
public Map<String, Component> components;
|
||||
public ExeConf launch4j;
|
||||
public NettyConfig netty;
|
||||
public LauncherConf launcher;
|
||||
public CertificateConf certificate;
|
||||
public JarSignerConf sign;
|
||||
public String startScript;
|
||||
private transient LaunchServer server = null;
|
||||
private transient AuthProviderPair authDefault;
|
||||
|
||||
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
||||
LaunchServerConfig newConfig = new LaunchServerConfig();
|
||||
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/"};
|
||||
newConfig.launch4j = new LaunchServerConfig.ExeConf();
|
||||
newConfig.launch4j.enabled = true;
|
||||
newConfig.launch4j.copyright = "© GravitLauncher Team";
|
||||
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
|
||||
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
|
||||
newConfig.launch4j.internalName = "Launcher";
|
||||
newConfig.launch4j.trademarks = "This product is licensed under GPLv3";
|
||||
newConfig.launch4j.txtFileVersion = "%s, build %d";
|
||||
newConfig.launch4j.txtProductVersion = "%s, build %d";
|
||||
newConfig.launch4j.productName = "GravitLauncher";
|
||||
newConfig.launch4j.productVer = newConfig.launch4j.fileVer;
|
||||
newConfig.launch4j.maxVersion = "1.8.999";
|
||||
newConfig.env = LauncherConfig.LauncherEnvironment.STD;
|
||||
newConfig.startScript = JVMHelper.OS_TYPE.equals(JVMHelper.OS.MUSTDIE) ? "." + File.separator + "start.bat" : "." + File.separator + "start.sh";
|
||||
newConfig.auth = new HashMap<>();
|
||||
AuthProviderPair a = new AuthProviderPair(new RejectAuthProvider("Настройте authProvider"),
|
||||
new MemoryAuthHandler(),
|
||||
new RequestTextureProvider("http://example.com/skins/%username%.png", "http://example.com/cloaks/%username%.png")
|
||||
);
|
||||
a.displayName = "Default";
|
||||
newConfig.auth.put("std", a);
|
||||
newConfig.protectHandler = new StdProtectHandler();
|
||||
newConfig.binaryName = "Launcher";
|
||||
|
||||
newConfig.netty = new NettyConfig();
|
||||
newConfig.netty.fileServerEnabled = true;
|
||||
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
|
||||
newConfig.netty.performance = new NettyPerformanceConfig();
|
||||
try {
|
||||
newConfig.netty.performance.usingEpoll = Epoll.isAvailable();
|
||||
} catch (Throwable e) {
|
||||
// Epoll class line 51+ catch (Exception) but Error will be thrown by System.load
|
||||
newConfig.netty.performance.usingEpoll = false;
|
||||
} // such as on ARM
|
||||
newConfig.netty.performance.bossThread = 2;
|
||||
newConfig.netty.performance.workerThread = 8;
|
||||
|
||||
newConfig.launcher = new LauncherConf();
|
||||
newConfig.launcher.guardType = "no";
|
||||
newConfig.launcher.compress = true;
|
||||
newConfig.launcher.warningMissArchJava = true;
|
||||
newConfig.launcher.attachLibraryBeforeProGuard = false;
|
||||
newConfig.launcher.deleteTempFiles = true;
|
||||
newConfig.launcher.enabledProGuard = true;
|
||||
newConfig.launcher.stripLineNumbers = true;
|
||||
newConfig.launcher.proguardGenMappings = true;
|
||||
|
||||
newConfig.certificate = new LaunchServerConfig.CertificateConf();
|
||||
newConfig.certificate.enabled = false;
|
||||
newConfig.sign = new JarSignerConf();
|
||||
|
||||
newConfig.components = new HashMap<>();
|
||||
AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent();
|
||||
authLimiterComponent.rateLimit = 3;
|
||||
authLimiterComponent.rateLimitMillis = 8000;
|
||||
authLimiterComponent.message = "Превышен лимит авторизаций";
|
||||
newConfig.components.put("authLimiter", authLimiterComponent);
|
||||
RegLimiterComponent regLimiterComponent = new RegLimiterComponent();
|
||||
regLimiterComponent.rateLimit = 3;
|
||||
regLimiterComponent.rateLimitMillis = 1000 * 60 * 60 * 10; //Блок на 10 часов
|
||||
regLimiterComponent.message = "Превышен лимит регистраций";
|
||||
newConfig.components.put("regLimiter", regLimiterComponent);
|
||||
newConfig.netty.sendExceptionEnabled = true;
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
public LaunchServerConfig setLaunchServer(LaunchServer server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String projectName;
|
||||
|
||||
public String[] mirrors;
|
||||
|
||||
public String binaryName;
|
||||
|
||||
public boolean copyBinaries = true;
|
||||
|
||||
public LauncherConfig.LauncherEnvironment env;
|
||||
|
||||
// Handlers & Providers
|
||||
|
||||
public Map<String, AuthProviderPair> auth;
|
||||
|
||||
public DaoProvider dao;
|
||||
|
||||
private transient AuthProviderPair authDefault;
|
||||
|
||||
public AuthProviderPair getAuthProviderPair(String name) {
|
||||
return auth.get(name);
|
||||
}
|
||||
|
||||
public ProtectHandler protectHandler;
|
||||
|
||||
public AuthProviderPair getAuthProviderPair() {
|
||||
if (authDefault != null) return authDefault;
|
||||
for (AuthProviderPair pair : auth.values()) {
|
||||
|
@ -70,17 +135,6 @@ public AuthProviderPair getAuthProviderPair() {
|
|||
throw new IllegalStateException("Default AuthProviderPair not found");
|
||||
}
|
||||
|
||||
public Map<String, Component> components;
|
||||
|
||||
public ExeConf launch4j;
|
||||
public NettyConfig netty;
|
||||
|
||||
public LauncherConf launcher;
|
||||
public CertificateConf certificate;
|
||||
public JarSignerConf sign;
|
||||
|
||||
public String startScript;
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
@ -93,7 +147,6 @@ public void setEnv(LauncherConfig.LauncherEnvironment env) {
|
|||
this.env = env;
|
||||
}
|
||||
|
||||
|
||||
public void verify() {
|
||||
if (auth == null || auth.size() < 1) {
|
||||
throw new NullPointerException("AuthProviderPair`s count should be at least one");
|
||||
|
@ -122,7 +175,7 @@ public void verify() {
|
|||
|
||||
public void init(LaunchServer.ReloadType type) {
|
||||
Launcher.applyLauncherEnv(env);
|
||||
for (Map.Entry<String,AuthProviderPair> provider : auth.entrySet()) {
|
||||
for (Map.Entry<String, AuthProviderPair> provider : auth.entrySet()) {
|
||||
provider.getValue().init(server, provider.getKey());
|
||||
}
|
||||
if (dao != null) {
|
||||
|
@ -170,10 +223,9 @@ public void close(LaunchServer.ReloadType type) {
|
|||
} catch (Exception e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
if(dao != null) {
|
||||
if (dao != null) {
|
||||
server.unregisterObject("dao", dao);
|
||||
if(dao instanceof AutoCloseable)
|
||||
{
|
||||
if (dao instanceof AutoCloseable) {
|
||||
try {
|
||||
((AutoCloseable) dao).close();
|
||||
} catch (Exception e) {
|
||||
|
@ -264,73 +316,4 @@ public NettyBindAddress(String address, int port) {
|
|||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
||||
LaunchServerConfig newConfig = new LaunchServerConfig();
|
||||
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/"};
|
||||
newConfig.launch4j = new LaunchServerConfig.ExeConf();
|
||||
newConfig.launch4j.enabled = true;
|
||||
newConfig.launch4j.copyright = "© GravitLauncher Team";
|
||||
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
|
||||
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
|
||||
newConfig.launch4j.internalName = "Launcher";
|
||||
newConfig.launch4j.trademarks = "This product is licensed under GPLv3";
|
||||
newConfig.launch4j.txtFileVersion = "%s, build %d";
|
||||
newConfig.launch4j.txtProductVersion = "%s, build %d";
|
||||
newConfig.launch4j.productName = "GravitLauncher";
|
||||
newConfig.launch4j.productVer = newConfig.launch4j.fileVer;
|
||||
newConfig.launch4j.maxVersion = "1.8.999";
|
||||
newConfig.env = LauncherConfig.LauncherEnvironment.STD;
|
||||
newConfig.startScript = JVMHelper.OS_TYPE.equals(JVMHelper.OS.MUSTDIE) ? "." + File.separator + "start.bat" : "." + File.separator + "start.sh";
|
||||
newConfig.auth = new HashMap<>();
|
||||
AuthProviderPair a = new AuthProviderPair(new RejectAuthProvider("Настройте authProvider"),
|
||||
new MemoryAuthHandler(),
|
||||
new RequestTextureProvider("http://example.com/skins/%username%.png", "http://example.com/cloaks/%username%.png")
|
||||
);
|
||||
a.displayName = "Default";
|
||||
newConfig.auth.put("std", a);
|
||||
newConfig.protectHandler = new StdProtectHandler();
|
||||
newConfig.binaryName = "Launcher";
|
||||
|
||||
newConfig.netty = new NettyConfig();
|
||||
newConfig.netty.fileServerEnabled = true;
|
||||
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
|
||||
newConfig.netty.performance = new NettyPerformanceConfig();
|
||||
try {
|
||||
newConfig.netty.performance.usingEpoll = Epoll.isAvailable();
|
||||
} catch (Throwable e) {
|
||||
// Epoll class line 51+ catch (Exception) but Error will be thrown by System.load
|
||||
newConfig.netty.performance.usingEpoll = false;
|
||||
} // such as on ARM
|
||||
newConfig.netty.performance.bossThread = 2;
|
||||
newConfig.netty.performance.workerThread = 8;
|
||||
|
||||
newConfig.launcher = new LauncherConf();
|
||||
newConfig.launcher.guardType = "no";
|
||||
newConfig.launcher.compress = true;
|
||||
newConfig.launcher.warningMissArchJava = true;
|
||||
newConfig.launcher.attachLibraryBeforeProGuard = false;
|
||||
newConfig.launcher.deleteTempFiles = true;
|
||||
newConfig.launcher.enabledProGuard = true;
|
||||
newConfig.launcher.stripLineNumbers = true;
|
||||
newConfig.launcher.proguardGenMappings = true;
|
||||
|
||||
newConfig.certificate = new LaunchServerConfig.CertificateConf();
|
||||
newConfig.certificate.enabled = false;
|
||||
newConfig.sign = new JarSignerConf();
|
||||
|
||||
newConfig.components = new HashMap<>();
|
||||
AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent();
|
||||
authLimiterComponent.rateLimit = 3;
|
||||
authLimiterComponent.rateLimitMillis = 8000;
|
||||
authLimiterComponent.message = "Превышен лимит авторизаций";
|
||||
newConfig.components.put("authLimiter", authLimiterComponent);
|
||||
RegLimiterComponent regLimiterComponent = new RegLimiterComponent();
|
||||
regLimiterComponent.rateLimit = 3;
|
||||
regLimiterComponent.rateLimitMillis = 1000 * 60 * 60 * 10; //Блок на 10 часов
|
||||
regLimiterComponent.message = "Превышен лимит регистраций";
|
||||
newConfig.components.put("regLimiter", regLimiterComponent);
|
||||
newConfig.netty.sendExceptionEnabled = true;
|
||||
return newConfig;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,24 @@
|
|||
|
||||
public interface User {
|
||||
String getUsername();
|
||||
|
||||
ClientPermissions getPermissions();
|
||||
|
||||
void setPermissions(ClientPermissions permissions);
|
||||
|
||||
boolean verifyPassword(String password);
|
||||
|
||||
void setPassword(String password);
|
||||
|
||||
String getAccessToken();
|
||||
|
||||
void setAccessToken(String accessToken);
|
||||
|
||||
String getServerID();
|
||||
|
||||
void setServerID(String serverID);
|
||||
|
||||
UUID getUuid();
|
||||
|
||||
void setUuid(UUID uuid);
|
||||
}
|
||||
|
|
|
@ -15,21 +15,21 @@
|
|||
@Entity(name = "User")
|
||||
@Table(name = "users")
|
||||
public class UserHibernateImpl implements User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
@Column(unique = true)
|
||||
public String username;
|
||||
public String email;
|
||||
@Column(unique = true)
|
||||
public UUID uuid;
|
||||
public String serverID;
|
||||
public long permissions;
|
||||
public long flags;
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
@Column(name = "password")
|
||||
private byte[] password;
|
||||
private String accessToken;
|
||||
public String serverID;
|
||||
private String password_salt;
|
||||
public long permissions;
|
||||
public long flags;
|
||||
|
||||
public void setPassword(String password) {
|
||||
password_salt = SecurityHelper.randomStringAESKey();
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.Reconfigurable;
|
||||
import pro.gravit.launchserver.dao.User;
|
||||
import pro.gravit.launchserver.dao.impl.UserHibernateImpl;
|
||||
import pro.gravit.launchserver.dao.impl.HibernateUserDAOImpl;
|
||||
import pro.gravit.launchserver.dao.impl.UserHibernateImpl;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.command.SubCommand;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
|
|
|
@ -27,12 +27,94 @@
|
|||
|
||||
public class SignHelper {
|
||||
|
||||
public static final OutputStream NULL = new OutputStream() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NullOutputStream";
|
||||
}
|
||||
|
||||
/** Discards the specified byte array. */
|
||||
@Override
|
||||
public void write(byte[] b) {
|
||||
}
|
||||
|
||||
/** Discards the specified byte array. */
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
}
|
||||
|
||||
/** Discards the specified byte. */
|
||||
@Override
|
||||
public void write(int b) {
|
||||
}
|
||||
|
||||
/** Never closes */
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
};
|
||||
public static final String hashFunctionName = "SHA-256";
|
||||
|
||||
private SignHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the KeyStore with given algo.
|
||||
*/
|
||||
public static KeyStore getStore(Path file, String storepass, String algo) throws IOException {
|
||||
try {
|
||||
KeyStore st = KeyStore.getInstance(algo);
|
||||
st.load(IOHelper.newInput(file), storepass != null ? storepass.toCharArray() : null);
|
||||
return st;
|
||||
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the beast that can actually sign the data (for JKS, for other make it).
|
||||
*/
|
||||
public static CMSSignedDataGenerator createSignedDataGenerator(KeyStore keyStore, String keyAlias, String signAlgo, String keyPassword) throws KeyStoreException, OperatorCreationException, CertificateEncodingException, UnrecoverableKeyException, NoSuchAlgorithmException, CMSException {
|
||||
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
|
||||
@SuppressWarnings("rawtypes")
|
||||
Store certStore = new JcaCertStore(certChain);
|
||||
Certificate cert = keyStore.getCertificate(keyAlias);
|
||||
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword != null ? keyPassword.toCharArray() : null);
|
||||
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
|
||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
||||
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
||||
generator.addSignerInfoGenerator(sig);
|
||||
generator.addCertificates(certStore);
|
||||
return generator;
|
||||
}
|
||||
|
||||
public static CMSSignedDataGenerator createSignedDataGenerator(PrivateKey privateKey, Certificate cert, List<Certificate> certChain, String signAlgo) throws OperatorCreationException, CertificateEncodingException, CMSException {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Store certStore = new JcaCertStore(certChain);
|
||||
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
|
||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
||||
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
||||
generator.addSignerInfoGenerator(sig);
|
||||
generator.addCertificates(certStore);
|
||||
return generator;
|
||||
}
|
||||
|
||||
public static MessageDigest hasher() {
|
||||
try {
|
||||
return MessageDigest.getInstance(hashFunctionName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper output stream that also sends the data to the given.
|
||||
*/
|
||||
public static class HashingOutputStream extends OutputStream {
|
||||
public final OutputStream out;
|
||||
public final MessageDigest hasher;
|
||||
public final OutputStream out;
|
||||
public final MessageDigest hasher;
|
||||
|
||||
public HashingOutputStream(OutputStream out, MessageDigest hasher) {
|
||||
this.out = out;
|
||||
|
@ -66,11 +148,12 @@ public void write(int b) throws IOException {
|
|||
out.write(b);
|
||||
hasher.update((byte) b);
|
||||
}
|
||||
|
||||
|
||||
public byte[] digest() {
|
||||
return hasher.digest();
|
||||
return hasher.digest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper output stream that also sends the data to the given.
|
||||
*/
|
||||
|
@ -81,88 +164,7 @@ public HashingNonClosingOutputStream(OutputStream out, MessageDigest hasher) {
|
|||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public static final OutputStream NULL = new OutputStream() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NullOutputStream";
|
||||
}
|
||||
|
||||
/** Discards the specified byte array. */
|
||||
@Override
|
||||
public void write(byte[] b) {
|
||||
}
|
||||
|
||||
/** Discards the specified byte array. */
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
}
|
||||
|
||||
/** Discards the specified byte. */
|
||||
@Override
|
||||
public void write(int b) {
|
||||
}
|
||||
|
||||
/** Never closes */
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
};
|
||||
private SignHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the KeyStore with given algo.
|
||||
*/
|
||||
public static KeyStore getStore(Path file, String storepass, String algo) throws IOException {
|
||||
try {
|
||||
KeyStore st = KeyStore.getInstance(algo);
|
||||
st.load(IOHelper.newInput(file), storepass != null ? storepass.toCharArray() : null);
|
||||
return st;
|
||||
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the beast that can actually sign the data (for JKS, for other make it).
|
||||
*/
|
||||
public static CMSSignedDataGenerator createSignedDataGenerator(KeyStore keyStore, String keyAlias, String signAlgo, String keyPassword) throws KeyStoreException, OperatorCreationException, CertificateEncodingException, UnrecoverableKeyException, NoSuchAlgorithmException, CMSException {
|
||||
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
|
||||
@SuppressWarnings("rawtypes")
|
||||
Store certStore = new JcaCertStore(certChain);
|
||||
Certificate cert = keyStore.getCertificate(keyAlias);
|
||||
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword != null ? keyPassword.toCharArray() : null);
|
||||
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
|
||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
||||
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
||||
generator.addSignerInfoGenerator(sig);
|
||||
generator.addCertificates(certStore);
|
||||
return generator;
|
||||
}
|
||||
public static CMSSignedDataGenerator createSignedDataGenerator(PrivateKey privateKey, Certificate cert, List<Certificate> certChain, String signAlgo) throws OperatorCreationException, CertificateEncodingException, CMSException {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Store certStore = new JcaCertStore(certChain);
|
||||
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
|
||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
||||
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
||||
generator.addSignerInfoGenerator(sig);
|
||||
generator.addCertificates(certStore);
|
||||
return generator;
|
||||
}
|
||||
|
||||
public static final String hashFunctionName = "SHA-256";
|
||||
|
||||
public static MessageDigest hasher() {
|
||||
try {
|
||||
return MessageDigest.getInstance(hashFunctionName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package pro.gravit.launchserver.launchermodules;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
public class LauncherModuleClassLoader extends URLClassLoader {
|
||||
public LauncherModuleClassLoader(ClassLoader parent) {
|
||||
super(new URL[0], parent);
|
||||
}
|
||||
|
||||
public void addURL(URL u) {
|
||||
super.addURL(u);
|
||||
}
|
||||
|
||||
public Class<?> rawDefineClass(String name, byte[] bytes) {
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package pro.gravit.launchserver.launchermodules;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class LauncherModuleLoader {
|
||||
public final List<ModuleEntity> launcherModules = new ArrayList<>();
|
||||
public final Path modulesDir;
|
||||
private final LaunchServer server;
|
||||
|
||||
public LauncherModuleLoader(LaunchServer server) {
|
||||
this.server = server; modulesDir = server.dir.resolve("launcher-modules");
|
||||
}
|
||||
|
||||
public void init() {
|
||||
if (!IOHelper.isDir(modulesDir)) {
|
||||
try {
|
||||
Files.createDirectories(modulesDir);
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
server.commandHandler.registerCommand("syncLauncherModules", new SyncLauncherModulesCommand(this));
|
||||
MainBuildTask mainTask = server.launcherBinary.getTaskByClass(MainBuildTask.class).get();
|
||||
mainTask.preBuildHook.registerHook((buildContext) -> {
|
||||
for (ModuleEntity e : launcherModules) {
|
||||
if (e.propertyMap != null) buildContext.task.properties.putAll(e.propertyMap);
|
||||
buildContext.clientModules.add(e.moduleMainClass);
|
||||
buildContext.readerClassPath.add(new JarFile(e.path.toFile()));
|
||||
}
|
||||
});
|
||||
mainTask.postBuildHook.registerHook((buildContext) -> {
|
||||
for (ModuleEntity e : launcherModules) {
|
||||
LogHelper.debug("Put %s launcher module", e.path.toString());
|
||||
buildContext.pushJarFile(e.path, (en) -> false, (en) -> true);
|
||||
}
|
||||
});
|
||||
try {
|
||||
syncModules();
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void syncModules() throws IOException {
|
||||
launcherModules.clear();
|
||||
IOHelper.walk(modulesDir, new ModulesVisitor(), false);
|
||||
}
|
||||
|
||||
static class ModuleEntity {
|
||||
public Path path;
|
||||
public String moduleMainClass;
|
||||
public String moduleConfigClass;
|
||||
public String moduleConfigName;
|
||||
public Map<String, Object> propertyMap;
|
||||
}
|
||||
|
||||
protected final class ModulesVisitor extends SimpleFileVisitor<Path> {
|
||||
private LauncherModuleClassLoader classLoader;
|
||||
|
||||
private ModulesVisitor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (file.toFile().getName().endsWith(".jar"))
|
||||
try (JarFile f = new JarFile(file.toFile())) {
|
||||
Attributes attributes = f.getManifest().getMainAttributes();
|
||||
String mainClass = attributes.getValue("Module-Main-Class");
|
||||
if (mainClass == null) {
|
||||
LogHelper.error("In module %s MainClass not found", file.toString());
|
||||
} else {
|
||||
ModuleEntity entity = new ModuleEntity();
|
||||
entity.path = file;
|
||||
entity.moduleMainClass = mainClass;
|
||||
entity.moduleConfigClass = attributes.getValue("Module-Config-Class");
|
||||
if (entity.moduleConfigClass != null) {
|
||||
entity.moduleConfigName = attributes.getValue("Module-Config-Name");
|
||||
if (entity.moduleConfigName == null) {
|
||||
LogHelper.warning("Module-Config-Name in module %s null. Module not configured", file.toString());
|
||||
} else {
|
||||
try {
|
||||
if (classLoader == null)
|
||||
classLoader = new LauncherModuleClassLoader(server.modulesManager.getModuleClassLoader());
|
||||
classLoader.addURL(file.toUri().toURL());
|
||||
Class<?> clazz = classLoader.loadClass(entity.moduleConfigClass);
|
||||
Path configPath = server.modulesManager.getConfigManager().getModuleConfig(entity.moduleConfigName);
|
||||
Object defaultConfig = MethodHandles.publicLookup().findStatic(clazz, "getDefault", MethodType.methodType(Object.class)).invoke();
|
||||
Object targetConfig;
|
||||
if (!Files.exists(configPath)) {
|
||||
LogHelper.debug("Write default config for module %s to %s", file.toString(), configPath.toString());
|
||||
try (Writer writer = IOHelper.newWriter(configPath)) {
|
||||
Launcher.gsonManager.configGson.toJson(defaultConfig, writer);
|
||||
}
|
||||
targetConfig = defaultConfig;
|
||||
} else {
|
||||
try (Reader reader = IOHelper.newReader(configPath)) {
|
||||
targetConfig = Launcher.gsonManager.configGson.fromJson(reader, clazz);
|
||||
}
|
||||
}
|
||||
Field[] fields = clazz.getFields();
|
||||
for (Field field : fields) {
|
||||
if ((field.getModifiers() & Modifier.STATIC) != 0) continue;
|
||||
Object obj = field.get(targetConfig);
|
||||
String configPropertyName = "modules.".concat(entity.moduleConfigName.toLowerCase()).concat(".").concat(field.getName().toLowerCase());
|
||||
if (entity.propertyMap == null) entity.propertyMap = new HashMap<>();
|
||||
LogHelper.dev("Property name %s", configPropertyName);
|
||||
entity.propertyMap.put(configPropertyName, obj);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
launcherModules.add(entity);
|
||||
}
|
||||
}
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package pro.gravit.launchserver.launchermodules;
|
||||
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
public class SyncLauncherModulesCommand extends Command {
|
||||
private final LauncherModuleLoader mod;
|
||||
|
||||
public SyncLauncherModulesCommand(LauncherModuleLoader mod) {
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "Resync launcher modules";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
mod.syncModules();
|
||||
LogHelper.info("Launcher Modules synced");
|
||||
}
|
||||
}
|
|
@ -20,11 +20,11 @@
|
|||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemReader;
|
||||
import org.bouncycastle.util.io.pem.PemWriter;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
|
@ -48,17 +48,13 @@
|
|||
import java.util.List;
|
||||
|
||||
public class CertificateManager {
|
||||
public X509CertificateHolder ca;
|
||||
public AsymmetricKeyParameter caKey;
|
||||
|
||||
public X509CertificateHolder server;
|
||||
public AsymmetricKeyParameter serverKey;
|
||||
|
||||
public LauncherTrustManager trustManager;
|
||||
|
||||
public final int validDays = 60;
|
||||
public final int minusHours = 6;
|
||||
|
||||
public X509CertificateHolder ca;
|
||||
public AsymmetricKeyParameter caKey;
|
||||
public X509CertificateHolder server;
|
||||
public AsymmetricKeyParameter serverKey;
|
||||
public LauncherTrustManager trustManager;
|
||||
public String orgName;
|
||||
|
||||
public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException {
|
||||
|
|
|
@ -14,26 +14,6 @@
|
|||
import java.util.Arrays;
|
||||
|
||||
public class MirrorManager {
|
||||
public static class Mirror {
|
||||
final String baseUrl;
|
||||
boolean enabled;
|
||||
|
||||
Mirror(String url) {
|
||||
//assetsURLMask = url.concat("assets/%s.zip");
|
||||
//clientsURLMask = url.concat("clients/%s.zip");
|
||||
baseUrl = url;
|
||||
}
|
||||
|
||||
private URL formatArgs(String mask, Object... args) throws MalformedURLException {
|
||||
Object[] data = Arrays.stream(args).map(e -> IOHelper.urlEncode(e.toString())).toArray();
|
||||
return new URL(baseUrl.concat(String.format(mask, data)));
|
||||
}
|
||||
|
||||
public URL getURL(String mask, Object... args) throws MalformedURLException {
|
||||
return formatArgs(mask, args);
|
||||
}
|
||||
}
|
||||
|
||||
protected final ArrayList<Mirror> list = new ArrayList<>();
|
||||
private Mirror defaultMirror;
|
||||
|
||||
|
@ -118,4 +98,24 @@ public JsonElement jsonRequest(JsonElement request, String method, String mask,
|
|||
}
|
||||
throw new IOException("Error jsonRequest. All mirrors return error");
|
||||
}
|
||||
|
||||
public static class Mirror {
|
||||
final String baseUrl;
|
||||
boolean enabled;
|
||||
|
||||
Mirror(String url) {
|
||||
//assetsURLMask = url.concat("assets/%s.zip");
|
||||
//clientsURLMask = url.concat("clients/%s.zip");
|
||||
baseUrl = url;
|
||||
}
|
||||
|
||||
private URL formatArgs(String mask, Object... args) throws MalformedURLException {
|
||||
Object[] data = Arrays.stream(args).map(e -> IOHelper.urlEncode(e.toString())).toArray();
|
||||
return new URL(baseUrl.concat(String.format(mask, data)));
|
||||
}
|
||||
|
||||
public URL getURL(String mask, Object... args) throws MalformedURLException {
|
||||
return formatArgs(mask, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,27 +10,6 @@
|
|||
import java.util.Map;
|
||||
|
||||
public class ReconfigurableManager {
|
||||
private static class ReconfigurableVirtualCommand extends Command {
|
||||
public ReconfigurableVirtualCommand(Map<String, Command> childs) {
|
||||
super(childs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
invokeSubcommands(args);
|
||||
}
|
||||
}
|
||||
|
||||
private final HashMap<String, Command> RECONFIGURABLE = new HashMap<>();
|
||||
|
||||
public void registerReconfigurable(String name, Reconfigurable reconfigurable) {
|
||||
|
@ -59,4 +38,25 @@ public void printHelp(String name) throws CommandException {
|
|||
public Map<String, Command> getCommands() {
|
||||
return RECONFIGURABLE;
|
||||
}
|
||||
|
||||
private static class ReconfigurableVirtualCommand extends Command {
|
||||
public ReconfigurableVirtualCommand(Map<String, Command> childs) {
|
||||
super(childs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
invokeSubcommands(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ public Client getOrNewClient(long session) {
|
|||
return clientSet.computeIfAbsent(session, Client::new);
|
||||
}
|
||||
|
||||
public Client removeClient(long session)
|
||||
{
|
||||
public Client removeClient(long session) {
|
||||
return clientSet.remove(session);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ public class AuthHookManager {
|
|||
public final BiHookSet<CheckServerResponse, Client> checkServerHook = new BiHookSet<>();
|
||||
public final BiHookSet<JoinServerResponse, Client> joinServerHook = new BiHookSet<>();
|
||||
public final BiHookSet<SetProfileResponse, Client> setProfileHook = new BiHookSet<>();
|
||||
public final HookSet<RegContext> registraion = new HookSet<>();
|
||||
|
||||
public static class RegContext {
|
||||
public final String login;
|
||||
|
@ -28,6 +29,4 @@ public RegContext(String login, String password, String ip, boolean trustContext
|
|||
this.trustContext = trustContext;
|
||||
}
|
||||
}
|
||||
|
||||
public final HookSet<RegContext> registraion = new HookSet<>();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package pro.gravit.launchserver.modules.impl;
|
||||
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
import pro.gravit.launcher.modules.LauncherModuleInfo;
|
||||
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -47,8 +47,8 @@ public enum Type {
|
|||
SERVER,
|
||||
USER
|
||||
}
|
||||
public static class TrustLevel
|
||||
{
|
||||
|
||||
public static class TrustLevel {
|
||||
public byte[] verifySecureKey;
|
||||
public boolean keyChecked;
|
||||
public byte[] publicKey;
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
import java.net.InetSocketAddress;
|
||||
|
||||
public class LauncherNettyServer implements AutoCloseable {
|
||||
private static final String WEBSOCKET_PATH = "/api";
|
||||
public final ServerBootstrap serverBootstrap;
|
||||
public final EventLoopGroup bossGroup;
|
||||
public final EventLoopGroup workerGroup;
|
||||
public final WebSocketService service;
|
||||
public final BiHookSet<NettyConnectContext, SocketChannel> pipelineHook = new BiHookSet<>();
|
||||
private static final String WEBSOCKET_PATH = "/api";
|
||||
|
||||
public LauncherNettyServer(LaunchServer server) {
|
||||
LaunchServerConfig.NettyConfig config = server.config.netty;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
|
||||
import pro.gravit.launchserver.socket.response.profile.ProfileByUsername;
|
||||
import pro.gravit.launchserver.socket.response.secure.GetSecureLevelInfoResponse;
|
||||
import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse;
|
||||
import pro.gravit.launchserver.socket.response.secure.SecurityReportResponse;
|
||||
import pro.gravit.launchserver.socket.response.secure.VerifySecureLevelKeyResponse;
|
||||
import pro.gravit.launchserver.socket.response.update.LauncherResponse;
|
||||
|
@ -35,22 +36,11 @@
|
|||
import java.lang.reflect.Type;
|
||||
|
||||
public class WebSocketService {
|
||||
public final ChannelGroup channels;
|
||||
public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
|
||||
|
||||
public static class WebSocketRequestContext {
|
||||
public final WebSocketServerResponse response;
|
||||
public final Client client;
|
||||
public final String ip;
|
||||
|
||||
public WebSocketRequestContext(WebSocketServerResponse response, Client client, String ip) {
|
||||
this.response = response;
|
||||
this.client = client;
|
||||
this.ip = ip;
|
||||
}
|
||||
}
|
||||
|
||||
public final ChannelGroup channels;
|
||||
public final BiHookSet<WebSocketRequestContext, ChannelHandlerContext> hook = new BiHookSet<>();
|
||||
private final LaunchServer server;
|
||||
private final Gson gson;
|
||||
|
||||
public WebSocketService(ChannelGroup channels, LaunchServer server) {
|
||||
this.channels = channels;
|
||||
|
@ -61,15 +51,36 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
|
|||
this.gson = Launcher.gsonManager.gson;
|
||||
}
|
||||
|
||||
private final LaunchServer server;
|
||||
private final Gson gson;
|
||||
public static void registerResponses() {
|
||||
providers.register("auth", AuthResponse.class);
|
||||
providers.register("checkServer", CheckServerResponse.class);
|
||||
providers.register("joinServer", JoinServerResponse.class);
|
||||
providers.register("profiles", ProfilesResponse.class);
|
||||
providers.register("launcher", LauncherResponse.class);
|
||||
providers.register("updateList", UpdateListResponse.class);
|
||||
providers.register("cmdExec", ExecCommandResponse.class);
|
||||
providers.register("setProfile", SetProfileResponse.class);
|
||||
providers.register("addLogListener", AddLogListenerResponse.class);
|
||||
providers.register("update", UpdateResponse.class);
|
||||
providers.register("restoreSession", RestoreSessionResponse.class);
|
||||
providers.register("batchProfileByUsername", BatchProfileByUsername.class);
|
||||
providers.register("profileByUsername", ProfileByUsername.class);
|
||||
providers.register("profileByUUID", ProfileByUUIDResponse.class);
|
||||
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
|
||||
providers.register("register", RegisterResponse.class);
|
||||
providers.register("setPassword", SetPasswordResponse.class);
|
||||
providers.register("exit", ExitResponse.class);
|
||||
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
|
||||
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
|
||||
providers.register("securityReport", SecurityReportResponse.class);
|
||||
providers.register("hardwareReport", HardwareReportResponse.class);
|
||||
}
|
||||
|
||||
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
|
||||
String request = frame.text();
|
||||
WebSocketServerResponse response = gson.fromJson(request, WebSocketServerResponse.class);
|
||||
if(response == null)
|
||||
{
|
||||
RequestEvent event= new ErrorRequestEvent("This type of request is not supported");
|
||||
if (response == null) {
|
||||
RequestEvent event = new ErrorRequestEvent("This type of request is not supported");
|
||||
sendObject(ctx, event);
|
||||
}
|
||||
process(ctx, response, client, ip);
|
||||
|
@ -109,30 +120,6 @@ public void registerClient(Channel channel) {
|
|||
channels.add(channel);
|
||||
}
|
||||
|
||||
public static void registerResponses() {
|
||||
providers.register("auth", AuthResponse.class);
|
||||
providers.register("checkServer", CheckServerResponse.class);
|
||||
providers.register("joinServer", JoinServerResponse.class);
|
||||
providers.register("profiles", ProfilesResponse.class);
|
||||
providers.register("launcher", LauncherResponse.class);
|
||||
providers.register("updateList", UpdateListResponse.class);
|
||||
providers.register("cmdExec", ExecCommandResponse.class);
|
||||
providers.register("setProfile", SetProfileResponse.class);
|
||||
providers.register("addLogListener", AddLogListenerResponse.class);
|
||||
providers.register("update", UpdateResponse.class);
|
||||
providers.register("restoreSession", RestoreSessionResponse.class);
|
||||
providers.register("batchProfileByUsername", BatchProfileByUsername.class);
|
||||
providers.register("profileByUsername", ProfileByUsername.class);
|
||||
providers.register("profileByUUID", ProfileByUUIDResponse.class);
|
||||
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
|
||||
providers.register("register", RegisterResponse.class);
|
||||
providers.register("setPassword", SetPasswordResponse.class);
|
||||
providers.register("exit", ExitResponse.class);
|
||||
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
|
||||
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
|
||||
providers.register("securityReport", SecurityReportResponse.class);
|
||||
}
|
||||
|
||||
public void sendObject(ChannelHandlerContext ctx, Object obj) {
|
||||
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ctx.voidPromise());
|
||||
}
|
||||
|
@ -173,6 +160,18 @@ public void sendEvent(EventResult obj) {
|
|||
channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)), ChannelMatchers.all(), true);
|
||||
}
|
||||
|
||||
public static class WebSocketRequestContext {
|
||||
public final WebSocketServerResponse response;
|
||||
public final Client client;
|
||||
public final String ip;
|
||||
|
||||
public WebSocketRequestContext(WebSocketServerResponse response, Client client, String ip) {
|
||||
this.response = response;
|
||||
this.client = client;
|
||||
this.ip = ip;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EventResult implements WebSocketEvent {
|
||||
public EventResult() {
|
||||
|
||||
|
|
|
@ -4,29 +4,30 @@
|
|||
import java.nio.file.Files;
|
||||
|
||||
public enum ContentType {
|
||||
NONE {
|
||||
@Override
|
||||
public String forPath(File p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
},
|
||||
NIO {
|
||||
@Override
|
||||
public String forPath(File p) {
|
||||
try {
|
||||
return Files.probeContentType(p.toPath());
|
||||
} catch (Throwable e) {
|
||||
return UNIVERSAL.forPath(p);
|
||||
}
|
||||
}
|
||||
},
|
||||
UNIVERSAL {
|
||||
@Override
|
||||
public String forPath(File p) {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
};
|
||||
public abstract String forPath(File p);
|
||||
NONE {
|
||||
@Override
|
||||
public String forPath(File p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
},
|
||||
NIO {
|
||||
@Override
|
||||
public String forPath(File p) {
|
||||
try {
|
||||
return Files.probeContentType(p.toPath());
|
||||
} catch (Throwable e) {
|
||||
return UNIVERSAL.forPath(p);
|
||||
}
|
||||
}
|
||||
},
|
||||
UNIVERSAL {
|
||||
@Override
|
||||
public String forPath(File p) {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public abstract String forPath(File p);
|
||||
}
|
||||
|
|
|
@ -7,21 +7,15 @@
|
|||
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Set;
|
||||
|
||||
// TODO refactor
|
||||
@SuppressWarnings("unused")
|
||||
public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
|
||||
private SSLServerSocketFactory ssf;
|
||||
|
||||
private transient final LaunchServer server;
|
||||
public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections");
|
||||
|
||||
public LauncherNettyServer nettyServer;
|
||||
|
||||
// API
|
||||
private Set<Socket> sockets;
|
||||
|
||||
private transient final LaunchServer server;
|
||||
private SSLServerSocketFactory ssf;
|
||||
|
||||
public NettyServerSocketHandler(LaunchServer server) {
|
||||
this.server = server;
|
||||
|
@ -29,7 +23,9 @@ public NettyServerSocketHandler(LaunchServer server) {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
//TODO: Close Impl
|
||||
if (nettyServer == null) return;
|
||||
nettyServer.close();
|
||||
nettyServer.service.channels.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,9 +15,14 @@
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
|
||||
static {
|
||||
}
|
||||
|
||||
public final LaunchServer srv;
|
||||
public final WebSocketService service;
|
||||
private final UUID connectUUID = UUID.randomUUID();
|
||||
public NettyConnectContext context;
|
||||
private Client client;
|
||||
|
||||
public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebSocketService service) {
|
||||
this.context = context;
|
||||
|
@ -25,20 +30,14 @@ public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebS
|
|||
this.service = service;
|
||||
}
|
||||
|
||||
private Client client;
|
||||
private final UUID connectUUID = UUID.randomUUID();
|
||||
|
||||
static {
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public final UUID getConnectUUID() {
|
||||
return connectUUID;
|
||||
}
|
||||
|
|
|
@ -28,13 +28,16 @@ public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpReque
|
|||
|
||||
public static final SimpleDateFormat dateFormatter;
|
||||
public static final String READ = "r";
|
||||
public static final int HTTP_CACHE_SECONDS = VerifyHelper.verifyInt(Integer.parseInt(System.getProperty("launcher.fileserver.cachesec", "60")), VerifyHelper.NOT_NEGATIVE, "HttpCache seconds should be positive");
|
||||
private static final boolean OLD_ALGO = Boolean.parseBoolean(System.getProperty("launcher.fileserver.oldalgo", "true"));
|
||||
private static final ContentType TYPE_PROBE = Arrays.stream(ContentType.values()).filter(e -> e.name().toLowerCase(Locale.US).equals(System.getProperty("launcher.fileserver.typeprobe", "nio"))).findFirst().orElse(ContentType.UNIVERSAL);
|
||||
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
|
||||
|
||||
static {
|
||||
dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
}
|
||||
public static final int HTTP_CACHE_SECONDS = VerifyHelper.verifyInt(Integer.parseInt(System.getProperty("launcher.fileserver.cachesec", "60")), VerifyHelper.NOT_NEGATIVE, "HttpCache seconds should be positive");
|
||||
private static final boolean OLD_ALGO = Boolean.parseBoolean(System.getProperty("launcher.fileserver.oldalgo", "true"));
|
||||
private static final ContentType TYPE_PROBE = Arrays.stream(ContentType.values()).filter(e -> e.name().toLowerCase(Locale.US).equals(System.getProperty("launcher.fileserver.typeprobe", "nio"))).findFirst().orElse(ContentType.UNIVERSAL);
|
||||
|
||||
private final Path base;
|
||||
private final boolean fullOut;
|
||||
private final boolean showHiddenFiles;
|
||||
|
@ -45,6 +48,120 @@ public FileServerHandler(Path base, boolean fullOut, boolean showHiddenFiles) {
|
|||
this.showHiddenFiles = showHiddenFiles;
|
||||
}
|
||||
|
||||
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath, boolean showHidden) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
|
||||
StringBuilder buf = new StringBuilder()
|
||||
.append("<!DOCTYPE html>\r\n")
|
||||
.append("<html><head><meta charset='utf-8' /><title>")
|
||||
.append("Listing of: ")
|
||||
.append(dirPath)
|
||||
.append("</title></head><body>\r\n")
|
||||
|
||||
.append("<h3>Listing of: ")
|
||||
.append(dirPath)
|
||||
.append("</h3>\r\n")
|
||||
|
||||
.append("<ul>")
|
||||
.append("<li><a href=\"../\">..</a></li>\r\n");
|
||||
|
||||
for (File f : dir.listFiles()) {
|
||||
if ((f.isHidden() && !showHidden) || !f.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = f.getName();
|
||||
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.append("<li><a href=\"")
|
||||
.append(name)
|
||||
.append("\">")
|
||||
.append(name)
|
||||
.append("</a></li>\r\n");
|
||||
}
|
||||
|
||||
buf.append("</ul></body></html>\r\n");
|
||||
ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
|
||||
response.content().writeBytes(buffer);
|
||||
buffer.release();
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
|
||||
response.headers().set(HttpHeaderNames.LOCATION, newUri);
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
|
||||
*
|
||||
* @param ctx Context
|
||||
*/
|
||||
private static void sendNotModified(ChannelHandlerContext ctx) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
|
||||
setDateHeader(response);
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Date header for the HTTP response
|
||||
*
|
||||
* @param response HTTP response
|
||||
*/
|
||||
private static void setDateHeader(FullHttpResponse response) {
|
||||
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(new Date(System.currentTimeMillis())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Date and Cache headers for the HTTP Response
|
||||
*
|
||||
* @param response HTTP response
|
||||
* @param fileToCache file to extract content type
|
||||
*/
|
||||
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
|
||||
// Date header
|
||||
Calendar time = new GregorianCalendar();
|
||||
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
|
||||
|
||||
// Add cache headers
|
||||
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
|
||||
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
|
||||
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
||||
response.headers().set(
|
||||
HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content type header for the HTTP Response
|
||||
*
|
||||
* @param response HTTP response
|
||||
* @param file file to extract content type
|
||||
*/
|
||||
private static void setContentTypeHeader(HttpResponse response, File file) {
|
||||
String contentType = TYPE_PROBE.forPath(file);
|
||||
if (contentType != null)
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
|
||||
if (!request.decoderResult().isSuccess()) {
|
||||
|
@ -154,120 +271,4 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|||
sendError(ctx, INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
|
||||
|
||||
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath, boolean showHidden) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
|
||||
StringBuilder buf = new StringBuilder()
|
||||
.append("<!DOCTYPE html>\r\n")
|
||||
.append("<html><head><meta charset='utf-8' /><title>")
|
||||
.append("Listing of: ")
|
||||
.append(dirPath)
|
||||
.append("</title></head><body>\r\n")
|
||||
|
||||
.append("<h3>Listing of: ")
|
||||
.append(dirPath)
|
||||
.append("</h3>\r\n")
|
||||
|
||||
.append("<ul>")
|
||||
.append("<li><a href=\"../\">..</a></li>\r\n");
|
||||
|
||||
for (File f : dir.listFiles()) {
|
||||
if ((f.isHidden() && !showHidden) || !f.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = f.getName();
|
||||
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.append("<li><a href=\"")
|
||||
.append(name)
|
||||
.append("\">")
|
||||
.append(name)
|
||||
.append("</a></li>\r\n");
|
||||
}
|
||||
|
||||
buf.append("</ul></body></html>\r\n");
|
||||
ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
|
||||
response.content().writeBytes(buffer);
|
||||
buffer.release();
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
|
||||
response.headers().set(HttpHeaderNames.LOCATION, newUri);
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
|
||||
*
|
||||
* @param ctx Context
|
||||
*/
|
||||
private static void sendNotModified(ChannelHandlerContext ctx) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
|
||||
setDateHeader(response);
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Date header for the HTTP response
|
||||
*
|
||||
* @param response HTTP response
|
||||
*/
|
||||
private static void setDateHeader(FullHttpResponse response) {
|
||||
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(new Date(System.currentTimeMillis())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Date and Cache headers for the HTTP Response
|
||||
*
|
||||
* @param response HTTP response
|
||||
* @param fileToCache file to extract content type
|
||||
*/
|
||||
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
|
||||
// Date header
|
||||
Calendar time = new GregorianCalendar();
|
||||
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
|
||||
|
||||
// Add cache headers
|
||||
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
|
||||
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
|
||||
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
||||
response.headers().set(
|
||||
HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content type header for the HTTP Response
|
||||
*
|
||||
* @param response HTTP response
|
||||
* @param file file to extract content type
|
||||
*/
|
||||
private static void setContentTypeHeader(HttpResponse response, File file) {
|
||||
String contentType = TYPE_PROBE.forPath(file);
|
||||
if (contentType != null)
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.auth.password.AuthECPassword;
|
||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||
|
@ -22,7 +21,6 @@
|
|||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -37,13 +35,6 @@ public class AuthResponse extends SimpleResponse {
|
|||
public String auth_id;
|
||||
public ConnectTypes authType;
|
||||
|
||||
public enum ConnectTypes {
|
||||
@Deprecated
|
||||
SERVER,
|
||||
CLIENT,
|
||||
API
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "auth";
|
||||
|
@ -68,8 +59,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
|||
AuthProviderPair pair;
|
||||
if (auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
|
||||
else pair = server.config.getAuthProviderPair(auth_id);
|
||||
if(pair == null)
|
||||
{
|
||||
if (pair == null) {
|
||||
sendError("auth_id incorrect");
|
||||
return;
|
||||
}
|
||||
|
@ -117,7 +107,22 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
|||
}
|
||||
}
|
||||
|
||||
public enum ConnectTypes {
|
||||
@Deprecated
|
||||
SERVER,
|
||||
CLIENT,
|
||||
API
|
||||
}
|
||||
|
||||
public static class AuthContext {
|
||||
public final String login;
|
||||
public final String profileName;
|
||||
public final String ip;
|
||||
public final ConnectTypes authType;
|
||||
public final Client client;
|
||||
@Deprecated
|
||||
public int password_length; //Use AuthProvider for get password
|
||||
|
||||
public AuthContext(Client client, String login, String profileName, String ip, ConnectTypes authType) {
|
||||
this.client = client;
|
||||
this.login = login;
|
||||
|
@ -125,13 +130,5 @@ public AuthContext(Client client, String login, String profileName, String ip, C
|
|||
this.ip = ip;
|
||||
this.authType = authType;
|
||||
}
|
||||
|
||||
public final String login;
|
||||
@Deprecated
|
||||
public int password_length; //Use AuthProvider for get password
|
||||
public final String profileName;
|
||||
public final String ip;
|
||||
public final ConnectTypes authType;
|
||||
public final Client client;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,7 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client pClient) {
|
||||
if(!pClient.isAuth || pClient.type == AuthResponse.ConnectTypes.CLIENT)
|
||||
{
|
||||
if (!pClient.isAuth || pClient.type == AuthResponse.ConnectTypes.CLIENT) {
|
||||
sendError("Permissions denied");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
public class ExitResponse extends SimpleResponse {
|
||||
public boolean exitAll;
|
||||
public String username;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "exit";
|
||||
|
@ -18,66 +19,56 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
if(username != null && ( !client.isAuth || client.permissions == null || !client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN) ))
|
||||
{
|
||||
if (username != null && (!client.isAuth || client.permissions == null || !client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN))) {
|
||||
sendError("Permissions denied");
|
||||
return;
|
||||
}
|
||||
if(username == null)
|
||||
{
|
||||
if(client.session == 0 && exitAll)
|
||||
{
|
||||
if (username == null) {
|
||||
if (client.session == 0 && exitAll) {
|
||||
sendError("Session invalid");
|
||||
return;
|
||||
}
|
||||
WebSocketFrameHandler handler = ctx.pipeline().get(WebSocketFrameHandler.class);
|
||||
if(handler == null)
|
||||
{
|
||||
if (handler == null) {
|
||||
sendError("Exit internal error");
|
||||
return;
|
||||
}
|
||||
Client newClient = new Client(0);
|
||||
newClient.checkSign = client.checkSign;
|
||||
handler.setClient(newClient);
|
||||
if(client.session != 0) server.sessionManager.removeClient(client.session);
|
||||
if(exitAll)
|
||||
{
|
||||
if (client.session != 0) server.sessionManager.removeClient(client.session);
|
||||
if (exitAll) {
|
||||
service.channels.forEach((channel) -> {
|
||||
if(channel == null || channel.pipeline() == null) return;
|
||||
if (channel == null || channel.pipeline() == null) return;
|
||||
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
|
||||
if(wsHandler == null || wsHandler == handler) return;
|
||||
if (wsHandler == null || wsHandler == handler) return;
|
||||
Client chClient = wsHandler.getClient();
|
||||
if(client.isAuth && client.username != null)
|
||||
{
|
||||
if(!chClient.isAuth || !client.username.equals(chClient.username)) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(chClient.session != client.session) return;
|
||||
if (client.isAuth && client.username != null) {
|
||||
if (!chClient.isAuth || !client.username.equals(chClient.username)) return;
|
||||
} else {
|
||||
if (chClient.session != client.session) return;
|
||||
}
|
||||
Client newCusClient = new Client(0);
|
||||
newCusClient.checkSign = chClient.checkSign;
|
||||
wsHandler.setClient(newCusClient);
|
||||
if(chClient.session != 0) server.sessionManager.removeClient(chClient.session);
|
||||
if (chClient.session != 0) server.sessionManager.removeClient(chClient.session);
|
||||
ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER);
|
||||
event.requestUUID = RequestEvent.eventUUID;
|
||||
wsHandler.service.sendObject(channel, event);
|
||||
});
|
||||
}
|
||||
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
service.channels.forEach((channel -> {
|
||||
if(channel == null || channel.pipeline() == null) return;
|
||||
if (channel == null || channel.pipeline() == null) return;
|
||||
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
|
||||
if(wsHandler == null) return;
|
||||
if (wsHandler == null) return;
|
||||
Client chClient = wsHandler.getClient();
|
||||
if(!chClient.isAuth || !username.equals(chClient.username)) return;
|
||||
if (!chClient.isAuth || !username.equals(chClient.username)) return;
|
||||
Client newCusClient = new Client(0);
|
||||
newCusClient.checkSign = chClient.checkSign;
|
||||
wsHandler.setClient(newCusClient);
|
||||
if(chClient.session != 0) server.sessionManager.removeClient(chClient.session);
|
||||
if (chClient.session != 0) server.sessionManager.removeClient(chClient.session);
|
||||
ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER);
|
||||
event.requestUUID = RequestEvent.eventUUID;
|
||||
wsHandler.service.sendObject(channel, event);
|
||||
|
|
|
@ -20,8 +20,7 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||
if(!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT)
|
||||
{
|
||||
if (!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT) {
|
||||
sendError("Permissions denied");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package pro.gravit.launchserver.socket.response.auth;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.events.request.ErrorRequestEvent;
|
||||
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
|
||||
|
@ -26,20 +25,15 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
|
||||
List<ClientProfile> profileList;
|
||||
List<ClientProfile> serverProfiles = server.getProfiles();
|
||||
if (server.config.protectHandler instanceof ProfilesProtectHandler)
|
||||
{
|
||||
if (server.config.protectHandler instanceof ProfilesProtectHandler) {
|
||||
ProfilesProtectHandler protectHandler = (ProfilesProtectHandler) server.config.protectHandler;
|
||||
profileList = new ArrayList<>(4);
|
||||
for(ClientProfile profile : serverProfiles)
|
||||
{
|
||||
if(protectHandler.canGetProfile(profile, client))
|
||||
{
|
||||
for (ClientProfile profile : serverProfiles) {
|
||||
if (protectHandler.canGetProfile(profile, client)) {
|
||||
profileList.add(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
profileList = serverProfiles;
|
||||
}
|
||||
sendResult(new ProfilesRequestEvent(profileList));
|
||||
|
|
|
@ -19,6 +19,12 @@ public class RegisterResponse extends SimpleResponse {
|
|||
public String email;
|
||||
public byte[] verifyHash;
|
||||
|
||||
public static byte[] registerHash(String login, String secret) throws NoSuchAlgorithmException {
|
||||
String text = login.concat("+").concat(secret);
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
return digest.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
byte[] normalHash = registerHash(login, server.runtime.registerApiKey);
|
||||
|
@ -43,10 +49,4 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
|||
public String getType() {
|
||||
return "register";
|
||||
}
|
||||
|
||||
public static byte[] registerHash(String login, String secret) throws NoSuchAlgorithmException {
|
||||
String text = login.concat("+").concat(secret);
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
return digest.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,6 @@
|
|||
import java.util.UUID;
|
||||
|
||||
public class BatchProfileByUsername extends SimpleResponse {
|
||||
static class Entry {
|
||||
String username;
|
||||
String client;
|
||||
}
|
||||
|
||||
Entry[] list;
|
||||
|
||||
@Override
|
||||
|
@ -36,4 +31,9 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
|||
}
|
||||
sendResult(result);
|
||||
}
|
||||
|
||||
static class Entry {
|
||||
String username;
|
||||
String client;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,21 +14,20 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
if(!(server.config.protectHandler instanceof SecureProtectHandler))
|
||||
{
|
||||
if (!(server.config.protectHandler instanceof SecureProtectHandler)) {
|
||||
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(null);
|
||||
response.enabled = false;
|
||||
sendResult(response);
|
||||
return;
|
||||
}
|
||||
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
|
||||
if(!secureProtectHandler.allowGetSecureLevelInfo(client))
|
||||
{
|
||||
if (!secureProtectHandler.allowGetSecureLevelInfo(client)) {
|
||||
sendError("Access denied");
|
||||
return;
|
||||
}
|
||||
if(client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
|
||||
if(client.trustLevel.verifySecureKey == null) client.trustLevel.verifySecureKey = secureProtectHandler.generateSecureLevelKey();
|
||||
if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
|
||||
if (client.trustLevel.verifySecureKey == null)
|
||||
client.trustLevel.verifySecureKey = secureProtectHandler.generateSecureLevelKey();
|
||||
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(client.trustLevel.verifySecureKey);
|
||||
response.enabled = true;
|
||||
sendResult(secureProtectHandler.onGetSecureLevelInfo(response));
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package pro.gravit.launchserver.socket.response.secure;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.request.secure.HardwareReportRequest;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||
|
||||
public class HardwareReportResponse extends SimpleResponse {
|
||||
public HardwareReportRequest.HardwareInfo hardware;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "hardwareReport";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ public class SecurityReportResponse extends SimpleResponse {
|
|||
public String largeData;
|
||||
public byte[] smallBytes;
|
||||
public byte[] largeBytes;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "securityReport";
|
||||
|
@ -20,8 +21,7 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
if(!(server.config.protectHandler instanceof SecureProtectHandler))
|
||||
{
|
||||
if (!(server.config.protectHandler instanceof SecureProtectHandler)) {
|
||||
sendError("Method not allowed");
|
||||
}
|
||||
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
public class VerifySecureLevelKeyResponse extends SimpleResponse {
|
||||
public byte[] publicKey;
|
||||
public byte[] signature;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "verifySecureLevelKey";
|
||||
|
@ -19,24 +20,20 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
if(!(server.config.protectHandler instanceof SecureProtectHandler) || client.trustLevel == null || client.trustLevel.verifySecureKey == null)
|
||||
{
|
||||
if (!(server.config.protectHandler instanceof SecureProtectHandler) || client.trustLevel == null || client.trustLevel.verifySecureKey == null) {
|
||||
sendError("This method not allowed");
|
||||
return;
|
||||
}
|
||||
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
|
||||
try {
|
||||
secureProtectHandler.verifySecureLevelKey(publicKey, client.trustLevel.verifySecureKey, signature);
|
||||
} catch (InvalidKeySpecException e)
|
||||
{
|
||||
} catch (InvalidKeySpecException e) {
|
||||
sendError("Invalid public key");
|
||||
return;
|
||||
} catch (SignatureException e)
|
||||
{
|
||||
} catch (SignatureException e) {
|
||||
sendError("Invalid signature");
|
||||
return;
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
} catch (SecurityException e) {
|
||||
sendError(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ private boolean checkSecure(String hash, String salt) {
|
|||
byte[] normal_hash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
|
||||
server.runtime.clientCheckSecret.concat(".").concat(salt));
|
||||
byte[] launcher_hash = Base64.getDecoder().decode(hash);
|
||||
//LogHelper.debug("[checkSecure] %s vs %s", Arrays.toString(normal_hash), Arrays.toString(launcher_hash));
|
||||
return Arrays.equals(normal_hash, launcher_hash);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.events.request.UpdateRequestEvent;
|
||||
import pro.gravit.launcher.hasher.HashedDir;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
|
||||
import pro.gravit.launchserver.config.LaunchServerConfig;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
public class UpdateResponse extends SimpleResponse {
|
||||
|
|
|
@ -12,40 +12,21 @@
|
|||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ASMTransformersTest {
|
||||
public static class ASMClassLoader extends ClassLoader
|
||||
{
|
||||
public ASMClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
public void rawDefineClass(String name, byte[] bytes, int offset, int length)
|
||||
{
|
||||
defineClass(name, bytes, offset, length);
|
||||
}
|
||||
}
|
||||
public static ASMClassLoader classLoader;
|
||||
public static class TestClass
|
||||
{
|
||||
@LauncherInject(value = "testprop")
|
||||
public int test;
|
||||
@LauncherInject(value = "testprop2")
|
||||
public List<String> s;
|
||||
@LauncherInject(value = "testprop3")
|
||||
public Map<String, String> map;
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void prepare() throws Throwable {
|
||||
classLoader = new ASMClassLoader(ASMTransformersTest.class.getClassLoader());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testASM() throws Throwable
|
||||
{
|
||||
void testASM() throws Throwable {
|
||||
ClassReader reader = new ClassReader(JarHelper.getClassBytes(TestClass.class));
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, ClassReader.SKIP_DEBUG);
|
||||
|
@ -69,11 +50,30 @@ void testASM() throws Throwable
|
|||
Class<?> clazz = classLoader.loadClass("ASMTestClass");
|
||||
Object instance = MethodHandles.publicLookup().findConstructor(clazz, MethodType.methodType(void.class)).invoke();
|
||||
Assertions.assertEquals(1234, (int)
|
||||
MethodHandles.publicLookup().findGetter(clazz, "test", int.class).invoke(instance));
|
||||
MethodHandles.publicLookup().findGetter(clazz, "test", int.class).invoke(instance));
|
||||
Assertions.assertEquals(strings, (List<String>)
|
||||
MethodHandles.publicLookup().findGetter(clazz, "s", List.class).invoke(instance));
|
||||
|
||||
Assertions.assertEquals(byteMap, (Map<String, Object>)
|
||||
MethodHandles.publicLookup().findGetter(clazz, "map", Map.class).invoke(instance));
|
||||
}
|
||||
|
||||
public static class ASMClassLoader extends ClassLoader {
|
||||
public ASMClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
public void rawDefineClass(String name, byte[] bytes, int offset, int length) {
|
||||
defineClass(name, bytes, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestClass {
|
||||
@LauncherInject(value = "testprop")
|
||||
public int test;
|
||||
@LauncherInject(value = "testprop2")
|
||||
public List<String> s;
|
||||
@LauncherInject(value = "testprop3")
|
||||
public Map<String, String> map;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package pro.gravit.launchserver;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
@ -28,7 +29,7 @@ public class StartLaunchServerTest {
|
|||
public static LaunchServer launchServer;
|
||||
|
||||
@BeforeAll
|
||||
public static void prepare() throws Exception {
|
||||
public static void prepare() throws Throwable {
|
||||
LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(modulesDir, configDir, null);
|
||||
LaunchServerConfig config = LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST);
|
||||
Launcher.gsonManager = new LaunchServerGsonManager(modulesManager);
|
||||
|
@ -48,12 +49,14 @@ public static void prepare() throws Exception {
|
|||
.setLaunchServerConfigManager(new LaunchServer.LaunchServerConfigManager() {
|
||||
@Override
|
||||
public LaunchServerConfig readConfig() {
|
||||
return null;
|
||||
return LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LaunchServerRuntimeConfig readRuntimeConfig() {
|
||||
return null;
|
||||
LaunchServerRuntimeConfig r = new LaunchServerRuntimeConfig();
|
||||
r.reset();
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,6 +74,11 @@ public void writeRuntimeConfig(LaunchServerRuntimeConfig config) {
|
|||
launchServer = builder.build();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void complete() throws Throwable {
|
||||
launchServer.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start() {
|
||||
launchServer.run();
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
javafx {
|
||||
version = "12"
|
||||
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
||||
modules = ['javafx.controls', 'javafx.fxml']
|
||||
}
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
|
|
@ -19,8 +19,8 @@ public class ClientLauncherWrapper {
|
|||
public static final String MAGIC_ARG = "-Djdk.attach.allowAttachSelf";
|
||||
public static final String WAIT_PROCESS_PROPERTY = "launcher.waitProcess";
|
||||
public static final String NO_JAVA9_CHECK_PROPERTY = "launcher.noJava9Check";
|
||||
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
|
||||
public static final boolean noJava9check = Boolean.getBoolean(NO_JAVA9_CHECK_PROPERTY);
|
||||
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
|
||||
|
||||
public static void main(String[] arguments) throws IOException, InterruptedException {
|
||||
LogHelper.printVersion("Launcher");
|
||||
|
@ -76,7 +76,7 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep
|
|||
if (!modulePath.isEmpty()) {
|
||||
args.add("--add-modules");
|
||||
String javaModules = "javafx.base,javafx.fxml,javafx.controls,jdk.unsupported";
|
||||
if(useSwing) javaModules = javaModules.concat(",javafx.swing");
|
||||
if (useSwing) javaModules = javaModules.concat(",javafx.swing");
|
||||
args.add(javaModules);
|
||||
args.add("--module-path");
|
||||
args.add(modulePath);
|
||||
|
|
|
@ -8,11 +8,11 @@ public abstract class JSApplication extends Application {
|
|||
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();
|
||||
|
||||
|
||||
public static JSApplication getInstance() {
|
||||
return INSTANCE.get();
|
||||
}
|
||||
|
||||
public JSApplication() {
|
||||
INSTANCE.set(this);
|
||||
}
|
||||
|
||||
public static JSApplication getInstance() {
|
||||
return INSTANCE.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
|
||||
public final class LauncherAgent {
|
||||
private static boolean isAgentStarted = false;
|
||||
public static Instrumentation inst;
|
||||
private static boolean isAgentStarted = false;
|
||||
|
||||
public static void addJVMClassPath(String path) throws IOException {
|
||||
LogHelper.debug("Launcher Agent addJVMClassPath");
|
||||
|
@ -25,10 +25,6 @@ public static void addJVMClassPath(Path path) throws IOException {
|
|||
inst.appendToSystemClassLoaderSearch(new JarFile(path.toFile()));
|
||||
}
|
||||
|
||||
public boolean isAgentStarted() {
|
||||
return isAgentStarted;
|
||||
}
|
||||
|
||||
public static void premain(String agentArgument, Instrumentation instrumentation) {
|
||||
System.out.println("Launcher Agent");
|
||||
checkAgentStacktrace();
|
||||
|
@ -58,4 +54,8 @@ public static void checkAgentStacktrace() {
|
|||
public static boolean isStarted() {
|
||||
return isAgentStarted;
|
||||
}
|
||||
|
||||
public boolean isAgentStarted() {
|
||||
return isAgentStarted;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package pro.gravit.launcher;
|
||||
|
||||
import pro.gravit.launcher.client.*;
|
||||
import pro.gravit.launcher.client.ClientLauncherEntryPoint;
|
||||
import pro.gravit.launcher.client.ClientLauncherProcess;
|
||||
import pro.gravit.launcher.client.ClientModuleManager;
|
||||
import pro.gravit.launcher.client.DirBridge;
|
||||
import pro.gravit.launcher.client.events.ClientEngineInitPhase;
|
||||
import pro.gravit.launcher.client.events.ClientExitPhase;
|
||||
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
|
||||
|
@ -33,6 +36,20 @@
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class LauncherEngine {
|
||||
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
|
||||
public static ClientLauncherProcess.ClientParams clientParams;
|
||||
public static LauncherGuardInterface guard;
|
||||
public static ClientModuleManager modulesManager;
|
||||
// Instance
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
public RuntimeProvider runtimeProvider;
|
||||
public ECPublicKey publicKey;
|
||||
public ECPrivateKey privateKey;
|
||||
|
||||
private LauncherEngine() {
|
||||
|
||||
}
|
||||
|
||||
//JVMHelper.getCertificates
|
||||
public static X509Certificate[] getCertificates(Class<?> clazz) {
|
||||
Object[] signers = clazz.getSigners();
|
||||
|
@ -40,10 +57,6 @@ public static X509Certificate[] getCertificates(Class<?> clazz) {
|
|||
return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new);
|
||||
}
|
||||
|
||||
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
|
||||
public static ClientLauncherProcess.ClientParams clientParams;
|
||||
public static LauncherGuardInterface guard;
|
||||
|
||||
public static void checkClass(Class<?> clazz) throws SecurityException {
|
||||
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
|
||||
if (trustManager == null) return;
|
||||
|
@ -58,8 +71,7 @@ public static void checkClass(Class<?> clazz) throws SecurityException {
|
|||
}
|
||||
}
|
||||
|
||||
public static void exitLauncher(int code)
|
||||
{
|
||||
public static void exitLauncher(int code) {
|
||||
modulesManager.invokeEvent(new ClientExitPhase(code));
|
||||
try {
|
||||
System.exit(code);
|
||||
|
@ -108,7 +120,22 @@ public static void initGson(ClientModuleManager modulesManager) {
|
|||
}
|
||||
|
||||
public static void verifyNoAgent() {
|
||||
if (JVMHelper.RUNTIME_MXBEAN.getInputArguments().stream().filter(e -> e != null && !e.isEmpty()).anyMatch(e -> e.contains("javaagent"))) throw new SecurityException("JavaAgent found");
|
||||
if (JVMHelper.RUNTIME_MXBEAN.getInputArguments().stream().filter(e -> e != null && !e.isEmpty()).anyMatch(e -> e.contains("javaagent")))
|
||||
throw new SecurityException("JavaAgent found");
|
||||
}
|
||||
|
||||
public static LauncherGuardInterface tryGetStdGuard() {
|
||||
switch (Launcher.getConfig().guardType) {
|
||||
case "no":
|
||||
return new LauncherNoGuard();
|
||||
case "wrapper":
|
||||
return new LauncherWrapperGuard();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static LauncherEngine clientInstance() {
|
||||
return new LauncherEngine();
|
||||
}
|
||||
|
||||
public void readKeys() throws IOException, InvalidKeySpecException {
|
||||
|
@ -133,20 +160,6 @@ public void readKeys() throws IOException, InvalidKeySpecException {
|
|||
}
|
||||
}
|
||||
|
||||
// Instance
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
public RuntimeProvider runtimeProvider;
|
||||
public ECPublicKey publicKey;
|
||||
public ECPrivateKey privateKey;
|
||||
|
||||
public static ClientModuleManager modulesManager;
|
||||
|
||||
private LauncherEngine() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void start(String... args) throws Throwable {
|
||||
//Launcher.modulesManager = new ClientModuleManager(this);
|
||||
LauncherEngine.guard = tryGetStdGuard();
|
||||
|
@ -188,20 +201,4 @@ public void start(String... args) throws Throwable {
|
|||
LogHelper.debug("Dir: %s", DirBridge.dir);
|
||||
runtimeProvider.run(args);
|
||||
}
|
||||
|
||||
public static LauncherGuardInterface tryGetStdGuard()
|
||||
{
|
||||
switch (Launcher.getConfig().guardType)
|
||||
{
|
||||
case "no":
|
||||
return new LauncherNoGuard();
|
||||
case "wrapper":
|
||||
return new LauncherWrapperGuard();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static LauncherEngine clientInstance() {
|
||||
return new LauncherEngine();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,14 @@
|
|||
import pro.gravit.launcher.hasher.HashedDir;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class NewLauncherSettings {
|
||||
@LauncherNetworkAPI
|
||||
public final transient List<HashedStoreEntry> lastHDirs = new ArrayList<>(16);
|
||||
@LauncherNetworkAPI
|
||||
public Map<String, UserSettings> userSettings = new HashMap<>();
|
||||
@LauncherNetworkAPI
|
||||
|
@ -14,6 +19,14 @@ public class NewLauncherSettings {
|
|||
@LauncherNetworkAPI
|
||||
public String consoleUnlockKey;
|
||||
|
||||
public void putHDir(String name, Path path, HashedDir dir) {
|
||||
String fullPath = path.toAbsolutePath().toString();
|
||||
lastHDirs.removeIf((e) -> e.fullPath.equals(fullPath) && e.name.equals(name));
|
||||
HashedStoreEntry e = new HashedStoreEntry(dir, name, fullPath);
|
||||
e.needSave = true;
|
||||
lastHDirs.add(e);
|
||||
}
|
||||
|
||||
public static class HashedStoreEntry {
|
||||
public final HashedDir hdir;
|
||||
public final String name;
|
||||
|
@ -26,15 +39,4 @@ public HashedStoreEntry(HashedDir hdir, String name, String fullPath) {
|
|||
this.fullPath = fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
@LauncherNetworkAPI
|
||||
public final transient List<HashedStoreEntry> lastHDirs = new ArrayList<>(16);
|
||||
|
||||
public void putHDir(String name, Path path, HashedDir dir) {
|
||||
String fullPath = path.toAbsolutePath().toString();
|
||||
lastHDirs.removeIf((e) -> e.fullPath.equals(fullPath) && e.name.equals(name));
|
||||
HashedStoreEntry e = new HashedStoreEntry(dir, name, fullPath);
|
||||
e.needSave = true;
|
||||
lastHDirs.add(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,16 @@ public class SystemService {
|
|||
private SystemService() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public static void exit(int code)
|
||||
{
|
||||
|
||||
public static void exit(int code) {
|
||||
LauncherEngine.exitLauncher(code);
|
||||
}
|
||||
public static void setSecurityManager(SecurityManager s)
|
||||
{
|
||||
|
||||
public static void setSecurityManager(SecurityManager s) {
|
||||
LogHelper.debug("Try set security manager %s", s == null ? "null" : s.getClass().getName());
|
||||
if(AuthService.profile == null || AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.NONE) return;
|
||||
if(AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.CLIENT)
|
||||
{
|
||||
if (AuthService.profile == null || AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.NONE)
|
||||
return;
|
||||
if (AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.CLIENT) {
|
||||
System.setSecurityManager(s);
|
||||
}
|
||||
//TODO NEXT
|
||||
|
|
|
@ -62,21 +62,21 @@ public ClientClassLoader(URL[] urls, ClassLoader parent) {
|
|||
public String findLibrary(String name) {
|
||||
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(getNativePrefix()).concat(name).concat(getNativeEx());
|
||||
}
|
||||
public String getNativeEx()
|
||||
{
|
||||
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||
|
||||
public String getNativeEx() {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||
return ".dll";
|
||||
else if(JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
else if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
return ".so";
|
||||
else if(JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
|
||||
else if (JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
|
||||
return ".dylib";
|
||||
return "";
|
||||
}
|
||||
public String getNativePrefix()
|
||||
{
|
||||
if(JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
|
||||
public String getNativePrefix() {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
return "lib";
|
||||
else if(JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
|
||||
else if (JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
|
||||
return "lib";
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -26,7 +26,10 @@
|
|||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.net.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -37,12 +40,12 @@
|
|||
import java.util.stream.Stream;
|
||||
|
||||
public class ClientLauncherEntryPoint {
|
||||
private static ClientClassLoader classLoader;
|
||||
|
||||
private static ClientLauncherProcess.ClientParams readParams(SocketAddress address) throws IOException {
|
||||
try (Socket socket = IOHelper.newSocket())
|
||||
{
|
||||
try (Socket socket = IOHelper.newSocket()) {
|
||||
socket.connect(address);
|
||||
try(HInput input = new HInput(socket.getInputStream()))
|
||||
{
|
||||
try (HInput input = new HInput(socket.getInputStream())) {
|
||||
byte[] serialized = input.readByteArray(0);
|
||||
ClientLauncherProcess.ClientParams params = Launcher.gsonManager.gson.fromJson(new String(serialized, IOHelper.UNICODE_CHARSET), ClientLauncherProcess.ClientParams.class);
|
||||
params.clientHDir = new HashedDir(input);
|
||||
|
@ -52,7 +55,7 @@ private static ClientLauncherProcess.ClientParams readParams(SocketAddress addre
|
|||
}
|
||||
}
|
||||
}
|
||||
private static ClientClassLoader classLoader;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
LauncherEngine.IS_CLIENT.set(true);
|
||||
LauncherEngine engine = LauncherEngine.clientInstance();
|
||||
|
@ -147,10 +150,12 @@ public static void main(String[] args) throws Throwable {
|
|||
launch(profile, params);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initGson(ClientModuleManager moduleManager) {
|
||||
Launcher.gsonManager = new ClientGsonManager(moduleManager);
|
||||
Launcher.gsonManager.initGson();
|
||||
}
|
||||
|
||||
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
||||
//if (matcher != null)
|
||||
// matcher = matcher.verifyOnly();
|
||||
|
@ -172,6 +177,7 @@ public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher,
|
|||
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkJVMBitsAndVersion() {
|
||||
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
|
||||
String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS);
|
||||
|
@ -188,6 +194,7 @@ public static void checkJVMBitsAndVersion() {
|
|||
JOptionPane.showMessageDialog(null, error);
|
||||
}
|
||||
}
|
||||
|
||||
private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException {
|
||||
return resolveClassPathStream(clientDir, classPath).collect(Collectors.toCollection(LinkedList::new));
|
||||
}
|
||||
|
@ -204,20 +211,7 @@ private static Stream<Path> resolveClassPathStream(Path clientDir, String... cla
|
|||
}
|
||||
return builder.build();
|
||||
}
|
||||
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Stream.Builder<Path> result;
|
||||
|
||||
private ClassPathFileVisitor(Stream.Builder<Path> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
|
||||
result.accept(file);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
private static void launch(ClientProfile profile, ClientLauncherProcess.ClientParams params) throws Throwable {
|
||||
// Add client args
|
||||
Collection<String> args = new LinkedList<>();
|
||||
|
@ -239,8 +233,7 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
|
|||
LogHelper.debug("Args: " + copy);
|
||||
// Resolve main class and method
|
||||
Class<?> mainClass = classLoader.loadClass(profile.getMainClass());
|
||||
for(URL u : classLoader.getURLs())
|
||||
{
|
||||
for (URL u : classLoader.getURLs()) {
|
||||
LogHelper.info("ClassLoader URL: %s", u.toString());
|
||||
}
|
||||
FMLPatcher.apply();
|
||||
|
@ -260,4 +253,19 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Stream.Builder<Path> result;
|
||||
|
||||
private ClassPathFileVisitor(Stream.Builder<Path> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
|
||||
result.accept(file);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,18 +24,19 @@
|
|||
import java.util.*;
|
||||
|
||||
public class ClientLauncherProcess {
|
||||
private transient Process process;
|
||||
private final transient Boolean[] waitWriteParams = new Boolean[] {false};
|
||||
public Path executeFile;
|
||||
public Path workDir;
|
||||
public Path javaDir;
|
||||
public final ClientParams params = new ClientParams();
|
||||
public final List<String> jvmArgs = new LinkedList<>();
|
||||
public final List<String> systemClientArgs = new LinkedList<>();
|
||||
public final List<String> systemClassPath = new LinkedList<>();
|
||||
public final Map<String, String> systemEnv = new HashMap<>();
|
||||
public final String mainClass;
|
||||
private final transient Boolean[] waitWriteParams = new Boolean[]{false};
|
||||
public Path executeFile;
|
||||
public Path workDir;
|
||||
public Path javaDir;
|
||||
public boolean useLegacyJavaClassPathProperty;
|
||||
public boolean isStarted;
|
||||
private transient Process process;
|
||||
|
||||
public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) {
|
||||
this.executeFile = executeFile;
|
||||
|
@ -74,8 +75,15 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
|
|||
this.params.javaHDir = jvmHDir;
|
||||
applyClientProfile();
|
||||
}
|
||||
private void applyClientProfile()
|
||||
{
|
||||
|
||||
public static String getPathSeparator() {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||
return ";";
|
||||
else
|
||||
return ":";
|
||||
}
|
||||
|
||||
private void applyClientProfile() {
|
||||
this.systemClassPath.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString());
|
||||
Collections.addAll(this.jvmArgs, this.params.profile.getJvmArgs());
|
||||
this.params.profile.pushOptionalJvmArgs(this.jvmArgs);
|
||||
|
@ -89,9 +97,68 @@ private void applyClientProfile()
|
|||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderCreateEvent(this));
|
||||
}
|
||||
|
||||
public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
||||
if (isStarted) throw new IllegalStateException("Process already started");
|
||||
if (LauncherEngine.guard != null) LauncherEngine.guard.applyGuardParams(this);
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderPreLaunchEvent(this));
|
||||
List<String> processArgs = new LinkedList<>();
|
||||
processArgs.add(executeFile.toString());
|
||||
processArgs.addAll(jvmArgs);
|
||||
//ADD CLASSPATH
|
||||
if (useLegacyJavaClassPathProperty) {
|
||||
processArgs.add("-Djava.class.path=".concat(String.join(getPathSeparator(), systemClassPath)));
|
||||
} else {
|
||||
processArgs.add("-cp");
|
||||
processArgs.add(String.join(getPathSeparator(), systemClassPath));
|
||||
}
|
||||
processArgs.add(mainClass);
|
||||
processArgs.addAll(systemClientArgs);
|
||||
synchronized (waitWriteParams) {
|
||||
if (!waitWriteParams[0]) {
|
||||
waitWriteParams.wait(1000);
|
||||
}
|
||||
}
|
||||
if (LogHelper.isDebugEnabled())
|
||||
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
|
||||
EnvHelper.addEnv(processBuilder);
|
||||
processBuilder.environment().put("JAVA_HOME", javaDir.toAbsolutePath().toString());
|
||||
processBuilder.environment().putAll(systemEnv);
|
||||
processBuilder.directory(workDir.toFile());
|
||||
processBuilder.inheritIO();
|
||||
if (pipeOutput) {
|
||||
processBuilder.redirectErrorStream(true);
|
||||
processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);
|
||||
}
|
||||
process = processBuilder.start();
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderLaunchedEvent(this));
|
||||
isStarted = true;
|
||||
}
|
||||
|
||||
public static class ClientParams
|
||||
{
|
||||
public void runWriteParams(SocketAddress address) throws IOException {
|
||||
try (ServerSocket serverSocket = new ServerSocket()) {
|
||||
serverSocket.bind(address);
|
||||
synchronized (waitWriteParams) {
|
||||
waitWriteParams[0] = true;
|
||||
waitWriteParams.notifyAll();
|
||||
}
|
||||
Socket socket = serverSocket.accept();
|
||||
try (HOutput output = new HOutput(socket.getOutputStream())) {
|
||||
byte[] serializedMainParams = Launcher.gsonManager.gson.toJson(params).getBytes(IOHelper.UNICODE_CHARSET);
|
||||
output.writeByteArray(serializedMainParams, 0);
|
||||
params.clientHDir.write(output);
|
||||
params.assetHDir.write(output);
|
||||
params.javaHDir.write(output);
|
||||
}
|
||||
}
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
|
||||
}
|
||||
|
||||
public Process getProcess() {
|
||||
return process;
|
||||
}
|
||||
|
||||
public static class ClientParams {
|
||||
public String assetDir;
|
||||
|
||||
public String clientDir;
|
||||
|
@ -128,27 +195,13 @@ public static class ClientParams
|
|||
|
||||
public transient HashedDir javaHDir;
|
||||
|
||||
public void addClientArgs(Collection<String> args)
|
||||
{
|
||||
public void addClientArgs(Collection<String> args) {
|
||||
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
||||
addModernClientArgs(args);
|
||||
else
|
||||
addClientLegacyArgs(args);
|
||||
}
|
||||
|
||||
|
||||
public static class ClientUserProperties {
|
||||
@LauncherNetworkAPI
|
||||
public String[] skinURL;
|
||||
@LauncherNetworkAPI
|
||||
public String[] skinDigest;
|
||||
@LauncherNetworkAPI
|
||||
public String[] cloakURL;
|
||||
@LauncherNetworkAPI
|
||||
public String[] cloakDigest;
|
||||
}
|
||||
|
||||
|
||||
public void addClientLegacyArgs(Collection<String> args) {
|
||||
args.add(playerProfile.username);
|
||||
args.add(accessToken);
|
||||
|
@ -211,74 +264,16 @@ private void addModernClientArgs(Collection<String> args) {
|
|||
Collections.addAll(args, "--height", Integer.toString(height));
|
||||
}
|
||||
}
|
||||
}
|
||||
public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
||||
if(isStarted) throw new IllegalStateException("Process already started");
|
||||
if(LauncherEngine.guard != null) LauncherEngine.guard.applyGuardParams(this);
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderPreLaunchEvent(this));
|
||||
List<String> processArgs = new LinkedList<>();
|
||||
processArgs.add(executeFile.toString());
|
||||
processArgs.addAll(jvmArgs);
|
||||
processArgs.add("-cp");
|
||||
//ADD CLASSPATH
|
||||
processArgs.add(String.join(getPathSeparator(), systemClassPath));
|
||||
processArgs.add(mainClass);
|
||||
processArgs.addAll(systemClientArgs);
|
||||
synchronized (waitWriteParams)
|
||||
{
|
||||
if(!waitWriteParams[0])
|
||||
{
|
||||
waitWriteParams.wait(1000);
|
||||
}
|
||||
}
|
||||
if(LogHelper.isDebugEnabled())
|
||||
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
|
||||
EnvHelper.addEnv(processBuilder);
|
||||
processBuilder.environment().put("JAVA_HOME", javaDir.toAbsolutePath().toString());
|
||||
processBuilder.environment().putAll(systemEnv);
|
||||
processBuilder.directory(workDir.toFile());
|
||||
processBuilder.inheritIO();
|
||||
if (pipeOutput) {
|
||||
processBuilder.redirectErrorStream(true);
|
||||
processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);
|
||||
}
|
||||
process = processBuilder.start();
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderLaunchedEvent(this));
|
||||
isStarted = true;
|
||||
}
|
||||
public void runWriteParams(SocketAddress address) throws IOException
|
||||
{
|
||||
try(ServerSocket serverSocket = new ServerSocket())
|
||||
{
|
||||
serverSocket.bind(address);
|
||||
synchronized (waitWriteParams)
|
||||
{
|
||||
waitWriteParams[0] = true;
|
||||
waitWriteParams.notifyAll();
|
||||
}
|
||||
Socket socket = serverSocket.accept();
|
||||
try(HOutput output = new HOutput(socket.getOutputStream()))
|
||||
{
|
||||
byte[] serializedMainParams = Launcher.gsonManager.gson.toJson(params).getBytes(IOHelper.UNICODE_CHARSET);
|
||||
output.writeByteArray(serializedMainParams, 0);
|
||||
params.clientHDir.write(output);
|
||||
params.assetHDir.write(output);
|
||||
params.javaHDir.write(output);
|
||||
}
|
||||
}
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
|
||||
}
|
||||
|
||||
public Process getProcess() {
|
||||
return process;
|
||||
}
|
||||
|
||||
public static String getPathSeparator()
|
||||
{
|
||||
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||
return ";";
|
||||
else
|
||||
return ":";
|
||||
public static class ClientUserProperties {
|
||||
@LauncherNetworkAPI
|
||||
public String[] skinURL;
|
||||
@LauncherNetworkAPI
|
||||
public String[] skinDigest;
|
||||
@LauncherNetworkAPI
|
||||
public String[] cloakURL;
|
||||
@LauncherNetworkAPI
|
||||
public String[] cloakDigest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package pro.gravit.launcher.client;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
|
|
@ -29,12 +29,34 @@ public class DirBridge {
|
|||
|
||||
public static boolean useLegacyDir;
|
||||
|
||||
|
||||
public static void move(Path newDir) throws IOException {
|
||||
IOHelper.move(dirUpdates, newDir);
|
||||
dirUpdates = newDir;
|
||||
static {
|
||||
String projectName = Launcher.getConfig().projectName;
|
||||
try {
|
||||
DirBridge.dir = getLauncherDir(projectName);
|
||||
if (!IOHelper.exists(DirBridge.dir)) Files.createDirectories(DirBridge.dir);
|
||||
DirBridge.defaultUpdatesDir = DirBridge.dir.resolve("updates");
|
||||
if (!IOHelper.exists(DirBridge.defaultUpdatesDir)) Files.createDirectories(DirBridge.defaultUpdatesDir);
|
||||
DirBridge.dirStore = getStoreDir(projectName);
|
||||
if (!IOHelper.exists(DirBridge.dirStore)) Files.createDirectories(DirBridge.dirStore);
|
||||
DirBridge.dirProjectStore = getProjectStoreDir(projectName);
|
||||
if (!IOHelper.exists(DirBridge.dirProjectStore)) Files.createDirectories(DirBridge.dirProjectStore);
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void move(Path newDir) throws IOException {
|
||||
if (newDir == null) {
|
||||
LogHelper.debug("Invalid dir (null)");
|
||||
if (LogHelper.isDevEnabled())
|
||||
LogHelper.dev(LogHelper.toString(new Throwable("Check stack of call DirBridge with null path...")));
|
||||
return;
|
||||
}
|
||||
Path oldUpdates = dirUpdates;
|
||||
dirUpdates = newDir;
|
||||
LogHelper.dev(newDir.toString());
|
||||
IOHelper.move(oldUpdates, dirUpdates);
|
||||
}
|
||||
|
||||
public static Path getAppDataDir() throws IOException {
|
||||
boolean isCustomDir = Boolean.getBoolean(System.getProperty(USE_CUSTOMDIR_PROPERTY, "false"));
|
||||
|
@ -53,6 +75,10 @@ public static Path getAppDataDir() throws IOException {
|
|||
return local;
|
||||
}
|
||||
} else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
|
||||
if (System.getenv().containsKey("appdata"))
|
||||
return Paths.get(System.getenv().get("appdata")).toAbsolutePath();
|
||||
if (System.getenv().containsKey("APPDATA")) // Because it is windows
|
||||
return Paths.get(System.getenv().get("APPDATA")).toAbsolutePath();
|
||||
Path appdata = IOHelper.HOME_DIR.resolve("AppData").resolve("Roaming");
|
||||
if (!IOHelper.isDir(appdata)) Files.createDirectories(appdata);
|
||||
return appdata;
|
||||
|
@ -65,12 +91,10 @@ public static Path getAppDataDir() throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static Path getLauncherDir(String projectname) throws IOException {
|
||||
return getAppDataDir().resolve(projectname);
|
||||
}
|
||||
|
||||
|
||||
public static Path getStoreDir(String projectname) throws IOException {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
return getAppDataDir().resolve("store");
|
||||
|
@ -80,39 +104,19 @@ else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
|||
return getAppDataDir().resolve("minecraftStore");
|
||||
}
|
||||
|
||||
|
||||
public static Path getProjectStoreDir(String projectname) throws IOException {
|
||||
return getStoreDir(projectname).resolve(projectname);
|
||||
}
|
||||
|
||||
|
||||
public static Path getGuardDir() {
|
||||
return dir.resolve("guard");
|
||||
}
|
||||
|
||||
|
||||
public static Path getLegacyLauncherDir(String projectname) {
|
||||
return IOHelper.HOME_DIR.resolve(projectname);
|
||||
}
|
||||
|
||||
|
||||
public static void setUseLegacyDir(boolean b) {
|
||||
useLegacyDir = b;
|
||||
}
|
||||
|
||||
static {
|
||||
String projectName = Launcher.getConfig().projectName;
|
||||
try {
|
||||
DirBridge.dir = getLauncherDir(projectName);
|
||||
if (!IOHelper.exists(DirBridge.dir)) Files.createDirectories(DirBridge.dir);
|
||||
DirBridge.defaultUpdatesDir = DirBridge.dir.resolve("updates");
|
||||
if (!IOHelper.exists(DirBridge.defaultUpdatesDir)) Files.createDirectories(DirBridge.defaultUpdatesDir);
|
||||
DirBridge.dirStore = getStoreDir(projectName);
|
||||
if (!IOHelper.exists(DirBridge.dirStore)) Files.createDirectories(DirBridge.dirStore);
|
||||
DirBridge.dirProjectStore = getProjectStoreDir(projectName);
|
||||
if (!IOHelper.exists(DirBridge.dirProjectStore)) Files.createDirectories(DirBridge.dirProjectStore);
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,34 +21,24 @@
|
|||
|
||||
public final class ServerPinger {
|
||||
|
||||
public static final class Result {
|
||||
|
||||
public final int onlinePlayers;
|
||||
|
||||
public final int maxPlayers;
|
||||
|
||||
public final String raw;
|
||||
|
||||
public Result(int onlinePlayers, int maxPlayers, String raw) {
|
||||
this.onlinePlayers = VerifyHelper.verifyInt(onlinePlayers,
|
||||
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
|
||||
this.maxPlayers = VerifyHelper.verifyInt(maxPlayers,
|
||||
VerifyHelper.NOT_NEGATIVE, "maxPlayers can't be < 0");
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
|
||||
public boolean isOverfilled() {
|
||||
return onlinePlayers >= maxPlayers;
|
||||
}
|
||||
}
|
||||
|
||||
// Constants
|
||||
private static final String LEGACY_PING_HOST_MAGIC = "§1";
|
||||
private static final String LEGACY_PING_HOST_CHANNEL = "MC|PingHost";
|
||||
private static final Pattern LEGACY_PING_HOST_DELIMETER = Pattern.compile("\0", Pattern.LITERAL);
|
||||
|
||||
private static final int PACKET_LENGTH = 65535;
|
||||
// Instance
|
||||
private final InetSocketAddress address;
|
||||
private final ClientProfile.Version version;
|
||||
// Cache
|
||||
private final Object cacheLock = new Object();
|
||||
private Result cache = null;
|
||||
private Exception cacheException = null;
|
||||
private Instant cacheTime = null;
|
||||
|
||||
public ServerPinger(ClientProfile profile) {
|
||||
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address");
|
||||
this.version = Objects.requireNonNull(profile.getVersion(), "version");
|
||||
}
|
||||
|
||||
private static String readUTF16String(HInput input) throws IOException {
|
||||
int length = input.readUnsignedShort() << 1;
|
||||
|
@ -61,24 +51,6 @@ private static void writeUTF16String(HOutput output, String s) throws IOExceptio
|
|||
output.stream.write(s.getBytes(StandardCharsets.UTF_16BE));
|
||||
}
|
||||
|
||||
// Instance
|
||||
private final InetSocketAddress address;
|
||||
private final ClientProfile.Version version;
|
||||
// Cache
|
||||
private final Object cacheLock = new Object();
|
||||
|
||||
private Result cache = null;
|
||||
|
||||
private Exception cacheException = null;
|
||||
|
||||
private Instant cacheTime = null;
|
||||
|
||||
|
||||
public ServerPinger(ClientProfile profile) {
|
||||
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address");
|
||||
this.version = Objects.requireNonNull(profile.getVersion(), "version");
|
||||
}
|
||||
|
||||
private Result doPing() throws IOException {
|
||||
try (Socket socket = IOHelper.newSocket()) {
|
||||
socket.connect(IOHelper.resolve(address), IOHelper.SOCKET_TIMEOUT);
|
||||
|
@ -192,7 +164,6 @@ private Result modernPing(HInput input, HOutput output) throws IOException {
|
|||
return new Result(online, max, response);
|
||||
}
|
||||
|
||||
|
||||
public Result ping() throws IOException {
|
||||
Instant now = Instant.now();
|
||||
synchronized (cacheLock) {
|
||||
|
@ -222,4 +193,26 @@ public Result ping() throws IOException {
|
|||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Result {
|
||||
|
||||
public final int onlinePlayers;
|
||||
|
||||
public final int maxPlayers;
|
||||
|
||||
public final String raw;
|
||||
|
||||
public Result(int onlinePlayers, int maxPlayers, String raw) {
|
||||
this.onlinePlayers = VerifyHelper.verifyInt(onlinePlayers,
|
||||
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
|
||||
this.maxPlayers = VerifyHelper.verifyInt(maxPlayers,
|
||||
VerifyHelper.NOT_NEGATIVE, "maxPlayers can't be < 0");
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
|
||||
public boolean isOverfilled() {
|
||||
return onlinePlayers >= maxPlayers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import pro.gravit.launcher.LauncherEngine;
|
||||
import pro.gravit.launcher.client.ClientLauncherProcess;
|
||||
import pro.gravit.launcher.modules.events.PostInitPhase;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
|
||||
public class ClientProcessReadyEvent extends PostInitPhase {
|
||||
public final LauncherEngine clientInstance;
|
||||
|
|
|
@ -21,8 +21,7 @@ public void invoke(String... args) throws Exception {
|
|||
verifyArgs(args, 1);
|
||||
if (ConsoleManager.checkUnlockKey(args[0])) {
|
||||
LogHelper.info("Unlock successful");
|
||||
if(!ConsoleManager.unlock())
|
||||
{
|
||||
if (!ConsoleManager.unlock()) {
|
||||
LogHelper.error("Console unlock canceled");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package pro.gravit.launcher.console.test;
|
||||
|
||||
import pro.gravit.launcher.utils.HWIDProvider;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
public class PrintHardwareInfoCommand extends Command {
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return "print your hardware info and timings";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
LogHelper.info("Your Hardware ID:");
|
||||
long startTime = System.currentTimeMillis();
|
||||
long currentTime;
|
||||
HWIDProvider provider = new HWIDProvider();
|
||||
currentTime = System.currentTimeMillis();
|
||||
LogHelper.info("Create HWIDProvider instance: %d ms", currentTime - startTime);
|
||||
startTime = System.currentTimeMillis();
|
||||
int bitness = provider.getBitness();
|
||||
long totalMemory = provider.getTotalMemory();
|
||||
boolean isBattery = provider.isBattery();
|
||||
currentTime = System.currentTimeMillis();
|
||||
LogHelper.info("Bitness: %d, totalMemory: %d(%.3f GB), battery %s, TIME: %d ms", bitness, totalMemory, (double) totalMemory / (1024.0 * 1024.0 * 1024.0), Boolean.toString(isBattery), currentTime - startTime);
|
||||
startTime = System.currentTimeMillis();
|
||||
int logicalProcessors = provider.getProcessorLogicalCount();
|
||||
int physicalProcessors = provider.getProcessorPhysicalCount();
|
||||
long processorMaxFreq = provider.getProcessorMaxFreq();
|
||||
currentTime = System.currentTimeMillis();
|
||||
LogHelper.info("Processors || logical: %d physical %d freq %d, TIME: %d ms", logicalProcessors, physicalProcessors, processorMaxFreq, currentTime - startTime);
|
||||
startTime = System.currentTimeMillis();
|
||||
String hwDiskID = provider.getHWDiskID();
|
||||
currentTime = System.currentTimeMillis();
|
||||
LogHelper.info("HWDiskID %s, TIME: %d ms", hwDiskID, currentTime - startTime);
|
||||
startTime = System.currentTimeMillis();
|
||||
String baseboardSerial = provider.getBaseboardSerialNumber();
|
||||
currentTime = System.currentTimeMillis();
|
||||
LogHelper.info("BaseboardSerial %s, TIME: %d ms", baseboardSerial, currentTime - startTime);
|
||||
LogHelper.info("Hardware ID end");
|
||||
}
|
||||
}
|
|
@ -1,10 +1,5 @@
|
|||
package pro.gravit.launcher.guard;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class LauncherGuardManager {
|
||||
public static LauncherGuardInterface guard;
|
||||
|
||||
|
|
|
@ -10,20 +10,6 @@
|
|||
|
||||
public class LauncherWrapperGuard implements LauncherGuardInterface {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "wrapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyGuardParams(ClientLauncherProcess process) {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
|
||||
String projectName = Launcher.getConfig().projectName;
|
||||
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
|
||||
process.executeFile = DirBridge.getGuardDir().resolve(wrapperUnpackName);
|
||||
}
|
||||
}
|
||||
|
||||
public LauncherWrapperGuard() {
|
||||
try {
|
||||
String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe";
|
||||
|
@ -36,4 +22,19 @@ public LauncherWrapperGuard() {
|
|||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "wrapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyGuardParams(ClientLauncherProcess process) {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
|
||||
String projectName = Launcher.getConfig().projectName;
|
||||
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
|
||||
process.executeFile = DirBridge.getGuardDir().resolve(wrapperUnpackName);
|
||||
process.useLegacyJavaClassPathProperty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import pro.gravit.launcher.LauncherEngine;
|
||||
import pro.gravit.launcher.client.events.ClientUnlockConsoleEvent;
|
||||
import pro.gravit.launcher.console.UnlockCommand;
|
||||
import pro.gravit.launcher.console.test.PrintHardwareInfoCommand;
|
||||
import pro.gravit.utils.command.CommandHandler;
|
||||
import pro.gravit.utils.command.JLineCommandHandler;
|
||||
import pro.gravit.utils.command.StdCommandHandler;
|
||||
|
@ -44,6 +45,7 @@ public static void registerCommands() {
|
|||
handler.registerCommand("gc", new GCCommand());
|
||||
handler.registerCommand("clear", new ClearCommand(handler));
|
||||
handler.registerCommand("unlock", new UnlockCommand());
|
||||
handler.registerCommand("printhardware", new PrintHardwareInfoCommand());
|
||||
}
|
||||
|
||||
public static boolean checkUnlockKey(String key) {
|
||||
|
@ -51,10 +53,10 @@ public static boolean checkUnlockKey(String key) {
|
|||
}
|
||||
|
||||
public static boolean unlock() {
|
||||
if(isConsoleUnlock) return true;
|
||||
if (isConsoleUnlock) return true;
|
||||
ClientUnlockConsoleEvent event = new ClientUnlockConsoleEvent(handler);
|
||||
LauncherEngine.modulesManager.invokeEvent(event);
|
||||
if(event.isCancel()) return false;
|
||||
if (event.isCancel()) return false;
|
||||
handler.registerCommand("debug", new DebugCommand());
|
||||
handler.unregisterCommand("unlock");
|
||||
isConsoleUnlock = true;
|
||||
|
|
|
@ -10,10 +10,68 @@
|
|||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class SettingsManager extends JsonConfigurable<NewLauncherSettings> {
|
||||
public static NewLauncherSettings settings;
|
||||
|
||||
|
||||
public SettingsManager() {
|
||||
super(NewLauncherSettings.class, DirBridge.dir.resolve("settings.json"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewLauncherSettings getConfig() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(NewLauncherSettings config) {
|
||||
settings = config;
|
||||
if (settings.consoleUnlockKey != null && !ConsoleManager.isConsoleUnlock) {
|
||||
if (ConsoleManager.checkUnlockKey(settings.consoleUnlockKey)) {
|
||||
ConsoleManager.unlock();
|
||||
LogHelper.info("Console auto unlocked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewLauncherSettings getDefaultConfig() {
|
||||
return new NewLauncherSettings();
|
||||
}
|
||||
|
||||
public void loadHDirStore(Path storePath) throws IOException {
|
||||
Files.createDirectories(storePath);
|
||||
IOHelper.walk(storePath, new StoreFileVisitor(), false);
|
||||
}
|
||||
|
||||
public void saveHDirStore(Path storeProjectPath) throws IOException {
|
||||
Files.createDirectories(storeProjectPath);
|
||||
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
|
||||
if (!e.needSave) continue;
|
||||
Path file = storeProjectPath.resolve(e.name.concat(".bin"));
|
||||
if (!Files.exists(file)) Files.createFile(file);
|
||||
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
|
||||
output.writeString(e.name, 128);
|
||||
output.writeString(e.fullPath, 1024);
|
||||
e.hdir.write(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadHDirStore() throws IOException {
|
||||
loadHDirStore(DirBridge.dirStore);
|
||||
}
|
||||
|
||||
public void saveHDirStore() throws IOException {
|
||||
saveHDirStore(DirBridge.dirProjectStore);
|
||||
}
|
||||
|
||||
public static class StoreFileVisitor extends SimpleFileVisitor<Path> {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
|
@ -30,66 +88,4 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static NewLauncherSettings settings;
|
||||
|
||||
public SettingsManager() {
|
||||
super(NewLauncherSettings.class, DirBridge.dir.resolve("settings.json"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NewLauncherSettings getConfig() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NewLauncherSettings getDefaultConfig() {
|
||||
return new NewLauncherSettings();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setConfig(NewLauncherSettings config) {
|
||||
settings = config;
|
||||
if (settings.consoleUnlockKey != null && !ConsoleManager.isConsoleUnlock) {
|
||||
if (ConsoleManager.checkUnlockKey(settings.consoleUnlockKey)) {
|
||||
ConsoleManager.unlock();
|
||||
LogHelper.info("Console auto unlocked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void loadHDirStore(Path storePath) throws IOException {
|
||||
Files.createDirectories(storePath);
|
||||
IOHelper.walk(storePath, new StoreFileVisitor(), false);
|
||||
}
|
||||
|
||||
|
||||
public void saveHDirStore(Path storeProjectPath) throws IOException {
|
||||
Files.createDirectories(storeProjectPath);
|
||||
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
|
||||
if (!e.needSave) continue;
|
||||
Path file = storeProjectPath.resolve(e.name.concat(".bin"));
|
||||
if (!Files.exists(file)) Files.createFile(file);
|
||||
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
|
||||
output.writeString(e.name, 128);
|
||||
output.writeString(e.fullPath, 1024);
|
||||
e.hdir.write(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void loadHDirStore() throws IOException {
|
||||
loadHDirStore(DirBridge.dirStore);
|
||||
}
|
||||
|
||||
|
||||
public void saveHDirStore() throws IOException {
|
||||
saveHDirStore(DirBridge.dirProjectStore);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,13 @@
|
|||
|
||||
public class FMLPatcher extends ClassLoader implements Opcodes {
|
||||
public static final MethodType EXITMH = MethodType.methodType(void.class, int.class);
|
||||
public static volatile FMLPatcher INSTANCE = null;
|
||||
public static final String[] PACKAGES = new String[]{"cpw.mods.fml.", "net.minecraftforge.fml.", "cpw.mods."};
|
||||
public static final Vector<MethodHandle> MHS = new Vector<>();
|
||||
public static volatile FMLPatcher INSTANCE = null;
|
||||
|
||||
public FMLPatcher(final ClassLoader cl) {
|
||||
super(cl);
|
||||
}
|
||||
|
||||
public static void apply() {
|
||||
INSTANCE = new FMLPatcher(null); // Never cause ClassFormatError (fuck forge 1.14!!!)
|
||||
|
@ -97,10 +101,6 @@ public static String randomStr(final int lenght) {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public FMLPatcher(final ClassLoader cl) {
|
||||
super(cl);
|
||||
}
|
||||
|
||||
public Class<?> def(final String name, final String exName) {
|
||||
return super.defineClass(name, ByteBuffer.wrap(gen(name.replace('.', '/'), exName)), null);
|
||||
}
|
||||
|
|
|
@ -18,60 +18,20 @@
|
|||
import java.util.Objects;
|
||||
|
||||
public final class DirWatcher implements Runnable, AutoCloseable {
|
||||
private final class RegisterFileVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
FileVisitResult result = super.preVisitDirectory(dir, attrs);
|
||||
if (DirWatcher.this.dir.equals(dir)) {
|
||||
dir.register(service, KINDS);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Maybe it's unnecessary to go deeper
|
||||
//if (matcher != null && !matcher.shouldVerify(path)) {
|
||||
// return FileVisitResult.SKIP_SUBTREE;
|
||||
//}
|
||||
|
||||
// Register
|
||||
dir.register(service, KINDS);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean FILE_TREE_SUPPORTED = JVMHelper.OS_TYPE == OS.MUSTDIE;
|
||||
|
||||
public static final String IGN_OVERFLOW = "launcher.dirwatcher.ignoreOverflows";
|
||||
// Constants
|
||||
private static final Kind<?>[] KINDS = {
|
||||
StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE
|
||||
};
|
||||
|
||||
public static final String IGN_OVERFLOW = "launcher.dirwatcher.ignoreOverflows";
|
||||
private static final boolean PROP_IGN_OVERFLOW = Boolean.parseBoolean(System.getProperty(IGN_OVERFLOW, "true"));
|
||||
|
||||
private static void handleError(Throwable e) {
|
||||
LogHelper.error(e);
|
||||
NativeJVMHalt.haltA(-123);
|
||||
}
|
||||
|
||||
private static Deque<String> toPath(Iterable<Path> path) {
|
||||
Deque<String> result = new LinkedList<>();
|
||||
for (Path pe : path)
|
||||
result.add(pe.toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final boolean PROP_IGN_OVERFLOW = Boolean.parseBoolean(System.getProperty(IGN_OVERFLOW, "true"));
|
||||
// Instance
|
||||
private final Path dir;
|
||||
private final HashedDir hdir;
|
||||
|
||||
private final FileNameMatcher matcher;
|
||||
|
||||
private final WatchService service;
|
||||
|
||||
private final boolean digest;
|
||||
|
||||
|
||||
public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
||||
this.dir = Objects.requireNonNull(dir, "dir");
|
||||
this.hdir = Objects.requireNonNull(hdir, "hdir");
|
||||
|
@ -84,6 +44,18 @@ public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean dig
|
|||
LogHelper.subInfo("DirWatcher %s", dir.toString());
|
||||
}
|
||||
|
||||
private static void handleError(Throwable e) {
|
||||
LogHelper.error(e);
|
||||
NativeJVMHalt.haltA(-123);
|
||||
}
|
||||
|
||||
private static Deque<String> toPath(Iterable<Path> path) {
|
||||
Deque<String> result = new LinkedList<>();
|
||||
for (Path pe : path)
|
||||
result.add(pe.toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
service.close();
|
||||
|
@ -136,4 +108,25 @@ public void run() {
|
|||
handleError(exc);
|
||||
}
|
||||
}
|
||||
|
||||
private final class RegisterFileVisitor extends SimpleFileVisitor<Path> {
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
FileVisitResult result = super.preVisitDirectory(dir, attrs);
|
||||
if (DirWatcher.this.dir.equals(dir)) {
|
||||
dir.register(service, KINDS);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Maybe it's unnecessary to go deeper
|
||||
//if (matcher != null && !matcher.shouldVerify(path)) {
|
||||
// return FileVisitResult.SKIP_SUBTREE;
|
||||
//}
|
||||
|
||||
// Register
|
||||
dir.register(service, KINDS);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue