mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-11-15 03:31:15 +03:00
[REFACTOR] Часть 1.
This commit is contained in:
parent
3ed166f8e7
commit
0d5dbe7794
169 changed files with 3716 additions and 4214 deletions
|
@ -117,7 +117,9 @@ 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-") &&
|
||||
|
|
|
@ -51,215 +51,48 @@
|
|||
|
||||
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;
|
||||
|
||||
public final Timer taskPool;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
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;
|
||||
|
@ -389,6 +222,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 +306,6 @@ private LauncherBinary binary() {
|
|||
return new EXELauncherBinary(this);
|
||||
}
|
||||
|
||||
|
||||
public void buildLauncherBinaries() throws IOException {
|
||||
launcherBinary.build();
|
||||
launcherEXEBinary.build();
|
||||
|
@ -435,7 +334,6 @@ public HashedDir getUpdateDir(String name) {
|
|||
return updatesDirMap.get(name);
|
||||
}
|
||||
|
||||
|
||||
public Set<Entry<String, HashedDir>> getUpdateDirs() {
|
||||
return updatesDirMap.entrySet();
|
||||
}
|
||||
|
@ -467,7 +365,6 @@ public void run() {
|
|||
modulesManager.invokeEvent(new LaunchServerFullInitEvent(this));
|
||||
}
|
||||
|
||||
|
||||
public void syncLauncherBinaries() throws IOException {
|
||||
LogHelper.info("Syncing launcher binaries");
|
||||
|
||||
|
@ -482,7 +379,6 @@ public void syncLauncherBinaries() throws IOException {
|
|||
|
||||
}
|
||||
|
||||
|
||||
public void syncProfilesDir() throws IOException {
|
||||
LogHelper.info("Syncing profiles dir");
|
||||
List<ClientProfile> newProfies = new LinkedList<>();
|
||||
|
@ -493,7 +389,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 +460,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;
|
||||
|
|
|
@ -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,33 +1,59 @@
|
|||
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;
|
||||
|
||||
public InjectClassAcceptor(Map<String, Object> values) {
|
||||
this.values = 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;
|
||||
|
||||
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 final Map<String, Object> values;
|
||||
|
||||
public InjectClassAcceptor(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
private static void visit(ClassNode classNode, Map<String, Object> values) {
|
||||
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name))
|
||||
|
@ -115,34 +141,6 @@ public void visit(final String name, final Object value) {
|
|||
});
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, Serializer<?>> serializers;
|
||||
|
||||
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
|
||||
|
@ -156,11 +154,6 @@ public InsnList serialize(Number value) {
|
|||
};
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Serializer<T> {
|
||||
InsnList serialize(T value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static InsnList serializeValue(Object value) {
|
||||
if (value == null) {
|
||||
|
@ -182,6 +175,16 @@ private static InsnList serializeValue(Object value) {
|
|||
value.getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(ClassNode classNode, String className, BuildContext context) {
|
||||
visit(classNode, values);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Serializer<T> {
|
||||
InsnList serialize(T value);
|
||||
}
|
||||
|
||||
private static class ListSerializer implements Serializer<List> {
|
||||
@Override
|
||||
public InsnList serialize(List value) {
|
||||
|
@ -237,9 +240,4 @@ public InsnList serialize(byte[] value) {
|
|||
return insnList;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(ClassNode classNode, String className, BuildContext context) {
|
||||
visit(classNode, values);
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -28,31 +28,29 @@ public void init(LaunchServer srv, String 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));
|
||||
if (textureProvider == null)
|
||||
throw new NullPointerException(String.format("Auth %s textureProvider null", name));
|
||||
provider.init(srv);
|
||||
handler.init(srv);
|
||||
}
|
||||
public void link(LaunchServer srv)
|
||||
{
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
@ -39,8 +39,7 @@ public boolean canGetUpdates(String updatesDirName, Client client) {
|
|||
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;
|
||||
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,10 +12,10 @@
|
|||
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();
|
||||
ECPublicKey pubKey = SecurityHelper.toPublicECKey(publicKey);
|
||||
|
@ -23,10 +23,12 @@ default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signatur
|
|||
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,29 @@ 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,16 +14,15 @@
|
|||
import java.util.Map;
|
||||
|
||||
public final class RejectAuthProvider extends AuthProvider implements Reconfigurable {
|
||||
private String message;
|
||||
private ArrayList<String> whitelist = new ArrayList<>();
|
||||
|
||||
public RejectAuthProvider() {
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
@ -75,119 +184,4 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,8 +14,6 @@
|
|||
import java.util.List;
|
||||
|
||||
public class ProguardConf {
|
||||
private static final char[] chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ".toCharArray();
|
||||
|
||||
public static final String[] JAVA9_OPTS = new String[]{
|
||||
"-libraryjars '<java.home>/jmods/'"
|
||||
};
|
||||
|
@ -25,6 +23,20 @@ public class ProguardConf {
|
|||
"-libraryjars '<java.home>/lib/ext/nashorn.jar'",
|
||||
"-libraryjars '<java.home>/lib/ext/jfxrt.jar'"
|
||||
};
|
||||
private static final char[] chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ".toCharArray();
|
||||
public final Path proguard;
|
||||
public final Path config;
|
||||
public final Path mappings;
|
||||
public final Path words;
|
||||
public transient final LaunchServer srv;
|
||||
public ProguardConf(LaunchServer srv) {
|
||||
proguard = srv.dir.resolve("proguard");
|
||||
config = proguard.resolve("proguard.config");
|
||||
mappings = proguard.resolve("mappings.pro");
|
||||
words = proguard.resolve("random.pro");
|
||||
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) {
|
||||
|
@ -35,20 +47,6 @@ private static String generateString(SecureRandom rand, String lowString, String
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public final Path proguard;
|
||||
public final Path config;
|
||||
public final Path mappings;
|
||||
public final Path words;
|
||||
public transient final LaunchServer srv;
|
||||
|
||||
public ProguardConf(LaunchServer srv) {
|
||||
proguard = srv.dir.resolve("proguard");
|
||||
config = proguard.resolve("proguard.config");
|
||||
mappings = proguard.resolve("mappings.pro");
|
||||
words = proguard.resolve("random.pro");
|
||||
this.srv = srv;
|
||||
}
|
||||
|
||||
public String[] buildConfig(Path inputJar, Path outputJar) {
|
||||
List<String> confStrs = new ArrayList<>();
|
||||
prepare(false);
|
||||
|
|
|
@ -41,20 +41,17 @@
|
|||
public class SignerJar implements AutoCloseable {
|
||||
|
||||
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 String manifestHash;
|
||||
private String manifestMainHash;
|
||||
|
||||
private final Map<String, String> fileDigests;
|
||||
|
||||
private final Map<String, String> sectionDigests;
|
||||
private final Supplier<CMSSignedDataGenerator> gen;
|
||||
private String manifestHash;
|
||||
private String manifestMainHash;
|
||||
|
||||
public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, String sig_fn, String sig_key_fn) {
|
||||
zos = out;
|
||||
|
|
|
@ -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()));
|
||||
|
@ -55,8 +41,7 @@ public static void apply(Path inputFile, Path addFile, ZipOutputStream output, L
|
|||
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,8 +32,10 @@
|
|||
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) {
|
||||
this.server = server;
|
||||
}
|
||||
|
@ -39,9 +44,6 @@ 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 {
|
||||
|
|
|
@ -27,96 +27,13 @@
|
|||
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) {
|
||||
server = srv;
|
||||
reader = new ClassMetadataReader();
|
||||
|
@ -171,8 +88,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());
|
||||
|
@ -204,42 +120,34 @@ protected void initProps() {
|
|||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (cn != null) {
|
||||
writer = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(writer);
|
||||
result = writer.toByteArray();
|
||||
|
@ -251,4 +159,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";
|
||||
|
@ -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,22 @@
|
|||
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 +79,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);
|
||||
}
|
||||
|
@ -57,4 +37,24 @@ public void invoke(String... args) throws Exception {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,7 @@ public void invoke(String... args) throws Exception {
|
|||
Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class);
|
||||
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");
|
||||
|
@ -172,8 +225,7 @@ public void close(LaunchServer.ReloadType type) {
|
|||
}
|
||||
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,6 +27,88 @@
|
|||
|
||||
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.
|
||||
*/
|
||||
|
@ -71,6 +153,7 @@ public byte[] digest() {
|
|||
return hasher.digest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper output stream that also sends the data to the given.
|
||||
*/
|
||||
|
@ -84,85 +167,4 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -35,22 +35,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,14 +50,34 @@ 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (response == null) {
|
||||
RequestEvent event = new ErrorRequestEvent("This type of request is not supported");
|
||||
sendObject(ctx, event);
|
||||
}
|
||||
|
@ -109,30 +118,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 +158,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() {
|
||||
|
||||
|
|
|
@ -28,5 +28,6 @@ public String forPath(File p) {
|
|||
}
|
||||
|
||||
};
|
||||
|
||||
public abstract String forPath(File p);
|
||||
}
|
||||
|
|
|
@ -7,19 +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;
|
||||
|
||||
private transient final LaunchServer server;
|
||||
private SSLServerSocketFactory ssf;
|
||||
|
||||
public NettyServerSocketHandler(LaunchServer server) {
|
||||
this.server = server;
|
||||
|
|
|
@ -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,118 +48,6 @@ public FileServerHandler(Path base, boolean fullOut, boolean showHiddenFiles) {
|
|||
this.showHiddenFiles = showHiddenFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
|
||||
if (!request.decoderResult().isSuccess()) {
|
||||
sendError(ctx, BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.method() != GET) {
|
||||
sendError(ctx, METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
final String uri = request.uri();
|
||||
final String path;
|
||||
|
||||
try {
|
||||
path = Paths.get(new URI(uri).getPath()).normalize().toString().substring(1);
|
||||
} catch (URISyntaxException e) {
|
||||
sendError(ctx, BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
File file = base.resolve(path).toFile();
|
||||
if ((file.isHidden() && !showHiddenFiles) || !file.exists()) {
|
||||
sendError(ctx, NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
if (fullOut) {
|
||||
if (uri.endsWith("/")) {
|
||||
sendListing(ctx, file, uri, showHiddenFiles);
|
||||
} else {
|
||||
sendRedirect(ctx, uri + '/');
|
||||
}
|
||||
} else sendError(ctx, NOT_FOUND); // can not handle dirs
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.isFile()) {
|
||||
sendError(ctx, FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache Validation
|
||||
String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE);
|
||||
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
||||
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
||||
|
||||
// Only compare up to the second because the datetime format we send to the client
|
||||
// does not have milliseconds
|
||||
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
|
||||
long fileLastModifiedSeconds = file.lastModified() / 1000;
|
||||
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
|
||||
sendNotModified(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RandomAccessFile raf;
|
||||
try {
|
||||
raf = new RandomAccessFile(file, READ);
|
||||
} catch (FileNotFoundException ignore) {
|
||||
sendError(ctx, NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
long fileLength = raf.length();
|
||||
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
HttpUtil.setContentLength(response, fileLength);
|
||||
setContentTypeHeader(response, file);
|
||||
setDateAndCacheHeaders(response, file);
|
||||
if (HttpUtil.isKeepAlive(request)) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
// Write the initial line and the header.
|
||||
ctx.write(response);
|
||||
|
||||
// Write the content.
|
||||
ChannelFuture sendFileFuture;
|
||||
ChannelFuture lastContentFuture;
|
||||
if (OLD_ALGO) {
|
||||
sendFileFuture =
|
||||
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
|
||||
// Write the end marker.
|
||||
lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
lastContentFuture.addListener(new ClosingChannelFutureListener(raf));
|
||||
} else {
|
||||
sendFileFuture =
|
||||
ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
|
||||
ctx.newProgressivePromise());
|
||||
// HttpChunkedInput will write the end marker (LastHttpContent) for us.
|
||||
lastContentFuture = sendFileFuture;
|
||||
}
|
||||
|
||||
// Decide whether to close the connection or not.
|
||||
if (!HttpUtil.isKeepAlive(request)) {
|
||||
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
if (ctx.channel().isActive()) {
|
||||
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");
|
||||
|
@ -270,4 +161,114 @@ private static void setContentTypeHeader(HttpResponse response, File 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()) {
|
||||
sendError(ctx, BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.method() != GET) {
|
||||
sendError(ctx, METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
final String uri = request.uri();
|
||||
final String path;
|
||||
|
||||
try {
|
||||
path = Paths.get(new URI(uri).getPath()).normalize().toString().substring(1);
|
||||
} catch (URISyntaxException e) {
|
||||
sendError(ctx, BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
File file = base.resolve(path).toFile();
|
||||
if ((file.isHidden() && !showHiddenFiles) || !file.exists()) {
|
||||
sendError(ctx, NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
if (fullOut) {
|
||||
if (uri.endsWith("/")) {
|
||||
sendListing(ctx, file, uri, showHiddenFiles);
|
||||
} else {
|
||||
sendRedirect(ctx, uri + '/');
|
||||
}
|
||||
} else sendError(ctx, NOT_FOUND); // can not handle dirs
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.isFile()) {
|
||||
sendError(ctx, FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache Validation
|
||||
String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE);
|
||||
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
||||
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
||||
|
||||
// Only compare up to the second because the datetime format we send to the client
|
||||
// does not have milliseconds
|
||||
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
|
||||
long fileLastModifiedSeconds = file.lastModified() / 1000;
|
||||
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
|
||||
sendNotModified(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RandomAccessFile raf;
|
||||
try {
|
||||
raf = new RandomAccessFile(file, READ);
|
||||
} catch (FileNotFoundException ignore) {
|
||||
sendError(ctx, NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
long fileLength = raf.length();
|
||||
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
HttpUtil.setContentLength(response, fileLength);
|
||||
setContentTypeHeader(response, file);
|
||||
setDateAndCacheHeaders(response, file);
|
||||
if (HttpUtil.isKeepAlive(request)) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
// Write the initial line and the header.
|
||||
ctx.write(response);
|
||||
|
||||
// Write the content.
|
||||
ChannelFuture sendFileFuture;
|
||||
ChannelFuture lastContentFuture;
|
||||
if (OLD_ALGO) {
|
||||
sendFileFuture =
|
||||
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
|
||||
// Write the end marker.
|
||||
lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
lastContentFuture.addListener(new ClosingChannelFutureListener(raf));
|
||||
} else {
|
||||
sendFileFuture =
|
||||
ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
|
||||
ctx.newProgressivePromise());
|
||||
// HttpChunkedInput will write the end marker (LastHttpContent) for us.
|
||||
lastContentFuture = sendFileFuture;
|
||||
}
|
||||
|
||||
// Decide whether to close the connection or not.
|
||||
if (!HttpUtil.isKeepAlive(request)) {
|
||||
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
if (ctx.channel().isActive()) {
|
||||
sendError(ctx, INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,21 @@ 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 +129,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,21 +19,17 @@ 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;
|
||||
}
|
||||
|
@ -40,19 +37,15 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
|||
newClient.checkSign = client.checkSign;
|
||||
handler.setClient(newClient);
|
||||
if (client.session != 0) server.sessionManager.removeClient(client.session);
|
||||
if(exitAll)
|
||||
{
|
||||
if (exitAll) {
|
||||
service.channels.forEach((channel) -> {
|
||||
if (channel == null || channel.pipeline() == null) return;
|
||||
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
|
||||
if (wsHandler == null || wsHandler == handler) return;
|
||||
Client chClient = wsHandler.getClient();
|
||||
if(client.isAuth && client.username != null)
|
||||
{
|
||||
if (client.isAuth && client.username != null) {
|
||||
if (!chClient.isAuth || !client.username.equals(chClient.username)) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if (chClient.session != client.session) return;
|
||||
}
|
||||
Client newCusClient = new Client(0);
|
||||
|
@ -65,9 +58,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
|||
});
|
||||
}
|
||||
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
service.channels.forEach((channel -> {
|
||||
if (channel == null || channel.pipeline() == null) return;
|
||||
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
|
||||
|
|
|
@ -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.verifySecureKey == null)
|
||||
client.trustLevel.verifySecureKey = secureProtectHandler.generateSecureLevelKey();
|
||||
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(client.trustLevel.verifySecureKey);
|
||||
response.enabled = true;
|
||||
sendResult(secureProtectHandler.onGetSecureLevelInfo(response));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -76,4 +57,23 @@ void testASM() throws Throwable
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,8 +62,8 @@ 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()
|
||||
{
|
||||
|
||||
public String getNativeEx() {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||
return ".dll";
|
||||
else if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
|
@ -72,8 +72,8 @@ else if(JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
|
|||
return ".dylib";
|
||||
return "";
|
||||
}
|
||||
public String getNativePrefix()
|
||||
{
|
||||
|
||||
public String getNativePrefix() {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
||||
return "lib";
|
||||
else if (JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
|
||||
|
|
|
@ -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,18 @@
|
|||
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 isStarted;
|
||||
private transient Process process;
|
||||
|
||||
public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) {
|
||||
this.executeFile = executeFile;
|
||||
|
@ -74,8 +74,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 +96,64 @@ 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);
|
||||
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 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 +190,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 +259,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,13 +29,27 @@ public class DirBridge {
|
|||
|
||||
public static boolean useLegacyDir;
|
||||
|
||||
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 {
|
||||
IOHelper.move(dirUpdates, newDir);
|
||||
dirUpdates = newDir;
|
||||
}
|
||||
|
||||
|
||||
public static Path getAppDataDir() throws IOException {
|
||||
boolean isCustomDir = Boolean.getBoolean(System.getProperty(USE_CUSTOMDIR_PROPERTY, "false"));
|
||||
if (isCustomDir) {
|
||||
|
@ -65,12 +79,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 +92,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;
|
||||
}
|
||||
|
|
|
@ -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,6 +10,19 @@
|
|||
|
||||
public class LauncherWrapperGuard implements LauncherGuardInterface {
|
||||
|
||||
public LauncherWrapperGuard() {
|
||||
try {
|
||||
String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe";
|
||||
String projectName = Launcher.getConfig().projectName;
|
||||
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
|
||||
String antiInjectName = JVMHelper.JVM_BITS == 64 ? "AntiInject64.dll" : "AntiInject32.dll";
|
||||
UnpackHelper.unpack(Launcher.getResourceURL(wrapperName, "guard"), DirBridge.getGuardDir().resolve(wrapperUnpackName));
|
||||
UnpackHelper.unpack(Launcher.getResourceURL(antiInjectName, "guard"), DirBridge.getGuardDir().resolve(antiInjectName));
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "wrapper";
|
||||
|
@ -23,17 +36,4 @@ public void applyGuardParams(ClientLauncherProcess process) {
|
|||
process.executeFile = DirBridge.getGuardDir().resolve(wrapperUnpackName);
|
||||
}
|
||||
}
|
||||
|
||||
public LauncherWrapperGuard() {
|
||||
try {
|
||||
String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe";
|
||||
String projectName = Launcher.getConfig().projectName;
|
||||
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
|
||||
String antiInjectName = JVMHelper.JVM_BITS == 64 ? "AntiInject64.dll" : "AntiInject32.dll";
|
||||
UnpackHelper.unpack(Launcher.getResourceURL(wrapperName, "guard"), DirBridge.getGuardDir().resolve(wrapperUnpackName));
|
||||
UnpackHelper.unpack(Launcher.getResourceURL(antiInjectName, "guard"), DirBridge.getGuardDir().resolve(antiInjectName));
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,20 +8,13 @@
|
|||
import java.awt.event.WindowEvent;
|
||||
|
||||
public final class NativeJVMHalt {
|
||||
public final int haltCode;
|
||||
|
||||
public NativeJVMHalt(int haltCode) {
|
||||
this.haltCode = haltCode;
|
||||
System.out.println("JVM exit code " + haltCode);
|
||||
}
|
||||
|
||||
public final int haltCode;
|
||||
|
||||
public native void aaabbb38C_D();
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private boolean aaabBooleanC_D() {
|
||||
return (boolean) (Boolean) null;
|
||||
}
|
||||
|
||||
public static void haltA(int code) {
|
||||
Throwable[] th = new Throwable[3];
|
||||
NativeJVMHalt halt = new NativeJVMHalt(code);
|
||||
|
@ -40,8 +33,7 @@ public static void haltA(int code) {
|
|||
} catch (Throwable fmlExc) {
|
||||
th[2] = fmlExc;
|
||||
}
|
||||
for(Throwable t : th)
|
||||
{
|
||||
for (Throwable t : th) {
|
||||
if (t != null) LogHelper.error(t);
|
||||
}
|
||||
boolean a = halt.aaabBooleanC_D();
|
||||
|
@ -54,6 +46,13 @@ public static boolean initFunc() {
|
|||
return true;
|
||||
}
|
||||
|
||||
public native void aaabbb38C_D();
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private boolean aaabBooleanC_D() {
|
||||
return (boolean) (Boolean) null;
|
||||
}
|
||||
|
||||
public static class WindowShutdown extends JFrame {
|
||||
private static final long serialVersionUID = 6321323663070818367L;
|
||||
|
||||
|
|
|
@ -4,34 +4,11 @@
|
|||
import pro.gravit.launcher.serialize.HOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class ClientPermissions {
|
||||
public static final ClientPermissions DEFAULT = new ClientPermissions();
|
||||
public long permissions;
|
||||
public long flags;
|
||||
public enum PermissionConsts
|
||||
{
|
||||
ADMIN(0x01),
|
||||
MANAGEMENT(0x02);
|
||||
public final long mask;
|
||||
|
||||
PermissionConsts(long mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
}
|
||||
public enum FlagConsts
|
||||
{
|
||||
SYSTEM(0x01),
|
||||
BANNED(0x02),
|
||||
UNTRUSTED(0x04),
|
||||
HIDDEN(0x08);
|
||||
public final long mask;
|
||||
|
||||
FlagConsts(long mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
}
|
||||
|
||||
public ClientPermissions(HInput input) throws IOException {
|
||||
this(input.readLong());
|
||||
|
@ -50,62 +27,83 @@ public ClientPermissions(long permissions, long flags) {
|
|||
this.flags = flags;
|
||||
}
|
||||
|
||||
public long toLong() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public static ClientPermissions getSuperuserAccount() {
|
||||
ClientPermissions perm = new ClientPermissions();
|
||||
return perm;
|
||||
}
|
||||
|
||||
public long toLong() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void write(HOutput output) throws IOException {
|
||||
output.writeLong(toLong());
|
||||
}
|
||||
|
||||
//Read methods
|
||||
public final boolean isPermission(PermissionConsts con)
|
||||
{
|
||||
public final boolean isPermission(PermissionConsts con) {
|
||||
return (permissions & con.mask) != 0;
|
||||
}
|
||||
public final boolean isPermission(long mask)
|
||||
{
|
||||
|
||||
public final boolean isPermission(long mask) {
|
||||
return (permissions & mask) != 0;
|
||||
}
|
||||
public final boolean isFlag(FlagConsts con)
|
||||
{
|
||||
|
||||
public final boolean isFlag(FlagConsts con) {
|
||||
return (flags & con.mask) != 0;
|
||||
}
|
||||
public final boolean isFlag(long mask)
|
||||
{
|
||||
|
||||
public final boolean isFlag(long mask) {
|
||||
return (flags & mask) != 0;
|
||||
}
|
||||
|
||||
//Write methods
|
||||
public final void setPermission(PermissionConsts con, boolean value)
|
||||
{
|
||||
public final void setPermission(PermissionConsts con, boolean value) {
|
||||
if (value) this.permissions |= con.mask;
|
||||
else this.permissions &= ~con.mask;
|
||||
}
|
||||
public final void setPermission(long mask, boolean value)
|
||||
{
|
||||
|
||||
public final void setPermission(long mask, boolean value) {
|
||||
if (value) this.permissions |= mask;
|
||||
else this.permissions &= ~mask;
|
||||
}
|
||||
public final void setFlag(FlagConsts con, boolean value)
|
||||
{
|
||||
|
||||
public final void setFlag(FlagConsts con, boolean value) {
|
||||
if (value) this.flags |= con.mask;
|
||||
else this.flags &= ~con.mask;
|
||||
}
|
||||
public final void setFlag(long mask, boolean value)
|
||||
{
|
||||
|
||||
public final void setFlag(long mask, boolean value) {
|
||||
if (value) this.flags |= mask;
|
||||
else this.flags &= ~mask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("permissions %d | flags %d", permissions, flags);
|
||||
}
|
||||
|
||||
public enum PermissionConsts {
|
||||
ADMIN(0x01),
|
||||
MANAGEMENT(0x02);
|
||||
public final long mask;
|
||||
|
||||
PermissionConsts(long mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum FlagConsts {
|
||||
SYSTEM(0x01),
|
||||
BANNED(0x02),
|
||||
UNTRUSTED(0x04),
|
||||
HIDDEN(0x08);
|
||||
public final long mask;
|
||||
|
||||
FlagConsts(long mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,29 +30,19 @@ public final class Launcher {
|
|||
public static final String CLOAK_DIGEST_PROPERTY = "cloakDigest";
|
||||
// Used to determine from clientside is launched from launcher
|
||||
public static final AtomicBoolean LAUNCHED = new AtomicBoolean(false);
|
||||
|
||||
private static final AtomicReference<LauncherConfig> CONFIG = new AtomicReference<>();
|
||||
|
||||
public static final int PROTOCOL_MAGIC_LEGACY = 0x724724_00 + 24;
|
||||
|
||||
public static final int PROTOCOL_MAGIC = 0xA205B064; // e = 2.718281828
|
||||
|
||||
// Constants
|
||||
|
||||
public static final String RUNTIME_DIR = "runtime";
|
||||
|
||||
// Constants
|
||||
public static final String GUARD_DIR = "guard";
|
||||
|
||||
public static final String CONFIG_FILE = "config.bin";
|
||||
|
||||
public static ClientProfile profile;
|
||||
|
||||
public static final String INIT_SCRIPT_FILE = "init.js";
|
||||
|
||||
public static final String API_SCRIPT_FILE = "engine/api.js";
|
||||
public static final String CONFIG_SCRIPT_FILE = "config.js";
|
||||
|
||||
private static final AtomicReference<LauncherConfig> CONFIG = new AtomicReference<>();
|
||||
private static final Pattern UUID_PATTERN = Pattern.compile("-", Pattern.LITERAL);
|
||||
public static ClientProfile profile;
|
||||
public static GsonManager gsonManager;
|
||||
|
||||
|
||||
|
|
|
@ -22,26 +22,16 @@ public final class LauncherConfig extends StreamObject {
|
|||
private static final List<byte[]> secureConfigCertificates = null;
|
||||
@LauncherInject("launcher.modules")
|
||||
private static final List<Class<?>> modulesClasses = null;
|
||||
@LauncherInject("launcher.address")
|
||||
public String address;
|
||||
private static final MethodType VOID_TYPE = MethodType.methodType(void.class);
|
||||
@LauncherInject("launcher.projectName")
|
||||
public final String projectName;
|
||||
@LauncherInject("launcher.port")
|
||||
public final int clientPort;
|
||||
@LauncherInject("runtimeconfig.secretKeyClient")
|
||||
public String secretKeyClient;
|
||||
@LauncherInject("runtimeconfig.oemUnlockKey")
|
||||
public String oemUnlockKey;
|
||||
public final LauncherTrustManager trustManager;
|
||||
|
||||
public final ECPublicKey publicKey;
|
||||
|
||||
|
||||
public final Map<String, byte[]> runtime;
|
||||
@LauncherInject("launcher.isWarningMissArchJava")
|
||||
public final boolean isWarningMissArchJava;
|
||||
@LauncherInject("launchercore.env")
|
||||
public LauncherEnvironment environment;
|
||||
@LauncherInject("launcher.guardType")
|
||||
public final String guardType;
|
||||
@LauncherInject("runtimeconfig.secureCheckHash")
|
||||
|
@ -50,6 +40,15 @@ public final class LauncherConfig extends StreamObject {
|
|||
public final String secureCheckSalt;
|
||||
@LauncherInject("runtimeconfig.passwordEncryptKey")
|
||||
public final String passwordEncryptKey;
|
||||
@LauncherInject("launcher.address")
|
||||
public String address;
|
||||
@LauncherInject("runtimeconfig.secretKeyClient")
|
||||
public String secretKeyClient;
|
||||
@LauncherInject("runtimeconfig.oemUnlockKey")
|
||||
public String oemUnlockKey;
|
||||
@LauncherInject("launchercore.env")
|
||||
public LauncherEnvironment environment;
|
||||
|
||||
|
||||
@LauncherInjectionConstructor
|
||||
public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException {
|
||||
|
@ -84,7 +83,6 @@ public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException
|
|||
runtime = Collections.unmodifiableMap(localResources);
|
||||
}
|
||||
|
||||
|
||||
public LauncherConfig(String address, ECPublicKey publicKey, Map<String, byte[]> runtime, String projectName) {
|
||||
this.address = address;
|
||||
this.publicKey = publicKey;
|
||||
|
@ -100,6 +98,17 @@ public LauncherConfig(String address, ECPublicKey publicKey, Map<String, byte[]>
|
|||
trustManager = null;
|
||||
}
|
||||
|
||||
public static void initModules(LauncherModulesManager modulesManager) {
|
||||
for (Class<?> clazz : modulesClasses)
|
||||
try {
|
||||
modulesManager.loadModule((LauncherModule) MethodHandles.publicLookup().findConstructor(clazz, VOID_TYPE).invokeWithArguments(Collections.emptyList()));
|
||||
} catch (Throwable e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
// This method should be called once at exec time.
|
||||
modulesClasses.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(HOutput output) throws IOException {
|
||||
output.writeByteArray(publicKey.getEncoded(), SecurityHelper.CRYPTO_MAX_LENGTH);
|
||||
|
@ -116,17 +125,4 @@ public void write(HOutput output) throws IOException {
|
|||
public enum LauncherEnvironment {
|
||||
DEV, DEBUG, STD, PROD
|
||||
}
|
||||
|
||||
private static final MethodType VOID_TYPE = MethodType.methodType(void.class);
|
||||
|
||||
public static void initModules(LauncherModulesManager modulesManager) {
|
||||
for (Class<?> clazz : modulesClasses)
|
||||
try {
|
||||
modulesManager.loadModule((LauncherModule) MethodHandles.publicLookup().findConstructor(clazz, VOID_TYPE).invokeWithArguments(Collections.emptyList()));
|
||||
} catch (Throwable e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
// This method should be called once at exec time.
|
||||
modulesClasses.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
import java.nio.file.Path;
|
||||
|
||||
public abstract class JsonConfigurable<T> implements JsonConfigurableInterface<T> {
|
||||
private transient final Type type;
|
||||
protected transient final Path configPath;
|
||||
private transient final Type type;
|
||||
|
||||
|
||||
public JsonConfigurable(Type type, Path configPath) {
|
||||
|
@ -26,9 +26,7 @@ public Type getType() {
|
|||
|
||||
public abstract T getConfig();
|
||||
|
||||
public abstract void setConfig(T config);
|
||||
|
||||
public abstract T getDefaultConfig();
|
||||
|
||||
|
||||
public abstract void setConfig(T config);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,11 @@ default void saveConfig(Gson gson, Path configPath) throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
default String toJsonString(Gson gson)
|
||||
{
|
||||
default String toJsonString(Gson gson) {
|
||||
return gson.toJson(getConfig(), getType());
|
||||
}
|
||||
|
||||
default String toJsonString()
|
||||
{
|
||||
default String toJsonString() {
|
||||
return toJsonString(Launcher.gsonManager.configGson);
|
||||
}
|
||||
|
||||
|
@ -91,12 +89,9 @@ default boolean generateConfigIfNotExists() throws IOException {
|
|||
|
||||
T getConfig();
|
||||
|
||||
|
||||
T getDefaultConfig();
|
||||
|
||||
|
||||
void setConfig(T config);
|
||||
|
||||
T getDefaultConfig();
|
||||
|
||||
Path getPath();
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
import java.nio.file.Path;
|
||||
|
||||
public abstract class SimpleConfig<T> implements JsonConfigurableInterface<T> {
|
||||
private transient final Class<T> type;
|
||||
protected transient final Path configPath;
|
||||
private transient final Class<T> type;
|
||||
|
||||
protected SimpleConfig(Class<T> type, Path configPath) {
|
||||
this.type = type;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
import java.nio.file.Path;
|
||||
|
||||
public class SimpleConfigurable<T> extends JsonConfigurable<T> {
|
||||
public T config;
|
||||
private final Class<T> tClass;
|
||||
public T config;
|
||||
|
||||
public SimpleConfigurable(Class<T> type, Path configPath) {
|
||||
super(type, configPath);
|
||||
|
@ -18,6 +18,11 @@ public T getConfig() {
|
|||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(T config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T getDefaultConfig() {
|
||||
|
@ -27,9 +32,4 @@ public T getDefaultConfig() {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(T config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
import pro.gravit.launcher.LauncherNetworkAPI;
|
||||
|
||||
public class ExceptionEvent extends RequestEvent {
|
||||
public ExceptionEvent(Exception e) {
|
||||
this.message = e.getMessage();
|
||||
this.clazz = e.getClass().getName();
|
||||
}
|
||||
|
||||
@LauncherNetworkAPI
|
||||
public final String message;
|
||||
@LauncherNetworkAPI
|
||||
public final String clazz;
|
||||
public ExceptionEvent(Exception e) {
|
||||
this.message = e.getMessage();
|
||||
this.clazz = e.getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
|
|
|
@ -9,11 +9,10 @@
|
|||
* The class of all request events sent by the server to the client
|
||||
*/
|
||||
public abstract class RequestEvent implements WebSocketEvent {
|
||||
public static final UUID eventUUID = UUID.fromString("fac0e2bd-9820-4449-b191-1d7c9bf781be");
|
||||
/**
|
||||
* UUID sent in request
|
||||
*/
|
||||
@LauncherNetworkAPI
|
||||
public UUID requestUUID;
|
||||
|
||||
public static final UUID eventUUID = UUID.fromString("fac0e2bd-9820-4449-b191-1d7c9bf781be");
|
||||
}
|
||||
|
|
|
@ -7,9 +7,6 @@
|
|||
|
||||
public class AuthRequestEvent extends RequestEvent {
|
||||
|
||||
public AuthRequestEvent() {
|
||||
}
|
||||
|
||||
@LauncherNetworkAPI
|
||||
public ClientPermissions permissions;
|
||||
@LauncherNetworkAPI
|
||||
|
@ -20,6 +17,8 @@ public AuthRequestEvent() {
|
|||
public String protectToken;
|
||||
@LauncherNetworkAPI
|
||||
public long session;
|
||||
public AuthRequestEvent() {
|
||||
}
|
||||
|
||||
public AuthRequestEvent(PlayerProfile pp, String accessToken, ClientPermissions permissions) {
|
||||
this.playerProfile = pp;
|
||||
|
|
|
@ -8,14 +8,13 @@
|
|||
|
||||
public class ErrorRequestEvent extends RequestEvent {
|
||||
public static UUID uuid = UUID.fromString("0af22bc7-aa01-4881-bdbb-dc62b3cdac96");
|
||||
@LauncherNetworkAPI
|
||||
public final String error;
|
||||
|
||||
public ErrorRequestEvent(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@LauncherNetworkAPI
|
||||
public final String error;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "error";
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
import pro.gravit.launcher.events.RequestEvent;
|
||||
|
||||
public class ExecCommandRequestEvent extends RequestEvent {
|
||||
@Override
|
||||
public String getType() {
|
||||
return "cmdExec";
|
||||
}
|
||||
|
||||
@LauncherNetworkAPI
|
||||
public final boolean success;
|
||||
|
||||
public ExecCommandRequestEvent(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "cmdExec";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
import pro.gravit.launcher.events.RequestEvent;
|
||||
|
||||
public class ExitRequestEvent extends RequestEvent {
|
||||
public enum ExitReason
|
||||
{
|
||||
SERVER, CLIENT, TIMEOUT, NO_EXIT
|
||||
}
|
||||
public final ExitReason reason;
|
||||
|
||||
public ExitRequestEvent(ExitReason reason) {
|
||||
|
@ -17,4 +13,8 @@ public ExitRequestEvent(ExitReason reason) {
|
|||
public String getType() {
|
||||
return "exit";
|
||||
}
|
||||
|
||||
public enum ExitReason {
|
||||
SERVER, CLIENT, TIMEOUT, NO_EXIT
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue