package sk.tomsik68.mclauncher.impl.login.yggdrasil;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.UUID;
import net.minidev.json.JSONObject;
import net.minidev.json.JSONStyle;
import net.minidev.json.JSONValue;
import sk.tomsik68.mclauncher.api.common.MCLauncherAPI;
import sk.tomsik68.mclauncher.api.json.IJSONSerializable;
import sk.tomsik68.mclauncher.api.login.ILoginService;
import sk.tomsik68.mclauncher.api.login.IProfile;
import sk.tomsik68.mclauncher.api.login.ISession;
import sk.tomsik68.mclauncher.api.login.LoginException;
import sk.tomsik68.mclauncher.api.services.IServicesAvailability;
import sk.tomsik68.mclauncher.impl.login.legacy.LegacyProfile;
import sk.tomsik68.mclauncher.util.FileUtils;
import sk.tomsik68.mclauncher.util.HttpUtils;
public final class YDLoginService implements ILoginService {
public static UUID clientToken = UUID.randomUUID();
private static final String PASSWORD_LOGIN_URL = "https://authserver.mojang.com/authenticate";
private static final String SESSION_LOGIN_URL = "https://authserver.mojang.com/refresh";
private static final String SESSION_LOGOUT_URL = "https://authserver.mojang.com/invalidate";
public YDLoginService() {
}
@Override
public ISession login(IProfile profile) throws YDServiceAuthenticationException {
MCLauncherAPI.log.fine("Logging in using yggdrassil...");
YDLoginResponse response;
if (profile instanceof LegacyProfile) {
response = doPasswordLogin(profile);
}
else if(profile instanceof YDAuthProfile) {
response = doSessionLogin(profile);
} else {
throw new IllegalArgumentException("YDLoginService can't deal with custom profile class: " + profile.getClass().getName());
}
MCLauncherAPI.log.fine("Login successful. Updating profile...");
YDSession result = new YDSession(response);
if(profile instanceof YDAuthProfile)
((YDAuthProfile)profile).update(result);
return result;
}
public IProfile createProfile(ISession session){
if(!(session instanceof YDSession)){
throw new IllegalArgumentException("Profile can only be created from an YDSession. Please use YDLoginService to log in.");
}
return new YDAuthProfile((YDSession) session);
}
private String doLoginPost(String url, IJSONSerializable request) throws YDServiceAuthenticationException {
String response = null;
try {
// Automatically Throws YDServiceAuthenticationException but will check for IOException and convert
response = HttpUtils.doJSONAuthenticationPost(url, request);
return response;
} catch (IOException e) {
throw new YDServiceAuthenticationException("Failed to authenticate using Mojang authentication service.", e);
}
}
// performs a HTTP POST request and checks if response from the system is error-less
private YDLoginResponse doCheckedLoginPost(String url, IJSONSerializable req) throws YDServiceAuthenticationException {
String jsonString = doLoginPost(url, req);
JSONObject jsonObject = (JSONObject)JSONValue.parse(jsonString);
YDLoginResponse response = new YDLoginResponse(jsonObject);
if(response.getError() != null) {
MCLauncherAPI.log.fine("Login response error. JSON STRING: '".concat(jsonString).concat("'"));
throw new YDServiceAuthenticationException("Authentication Failed: " + response.getMessage(),
new LoginException("Error ".concat(response.getError()).concat(" : ").concat(response.getMessage())));
}
return response;
}
private YDLoginResponse doSessionLogin(IProfile profile) throws YDServiceAuthenticationException {
MCLauncherAPI.log.fine("Using session ID login");
YDSessionLoginRequest request = new YDSessionLoginRequest(profile.getPassword(), clientToken.toString());
YDLoginResponse response = doCheckedLoginPost(SESSION_LOGIN_URL, request);
return response;
}
private YDLoginResponse doPasswordLogin(IProfile profile) throws YDServiceAuthenticationException {
MCLauncherAPI.log.fine("Using password-based login");
YDPasswordLoginRequest request = new YDPasswordLoginRequest(profile.getName(), profile.getPassword(), clientToken.toString());
YDLoginResponse response = doCheckedLoginPost(PASSWORD_LOGIN_URL, request);
return response;
}
@Override
public boolean isAvailable(IServicesAvailability availability) {
return availability.isServiceAvailable("auth.mojang.com");
}
public void save(File mcInstance) throws Exception {
File file = new File(mcInstance, "launcher_profiles.json");
saveTo(file);
}
public void saveTo(File file) throws Exception {
JSONObject obj = new JSONObject();
if (file.exists()) {
MCLauncherAPI.log.fine("The file already exists. YDLoginService won't overwrite client token.");
FileReader fileReader = new FileReader(file);
obj = (JSONObject) JSONValue.parse(fileReader);
fileReader.close();
if (obj.containsKey("clientToken"))
return;
file.delete();
}
FileUtils.createFileSafely(file);
MCLauncherAPI.log.fine("Writing client token...");
// file.createNewFile();
obj.put("clientToken", clientToken.toString());
FileWriter fw = new FileWriter(file);
obj.writeJSONString(fw, JSONStyle.NO_COMPRESS);
fw.flush();
fw.close();
}
public void load(File mcInstance) throws Exception {
File file = new File(mcInstance, "launcher_profiles.json");
loadFrom(file);
}
public void loadFrom(File file) throws Exception {
FileReader fileReader = new FileReader(file);
JSONObject obj = (JSONObject) JSONValue.parse(fileReader);
fileReader.close();
clientToken = UUID.fromString(obj.get("clientToken").toString());
MCLauncherAPI.log.fine("Loaded client token: " + clientToken.toString());
}
@Override
public void logout(ISession session) throws Exception {
YDLogoutRequest request = new YDLogoutRequest(session, clientToken);
String response = doLoginPost(SESSION_LOGOUT_URL, request);
if("".equals(response)) {
MCLauncherAPI.log.fine("Logout successful.");
} else {
MCLauncherAPI.log.fine("Unknown error occured during logout(mojang yggdrassil didn't return empty string).");
}
}
}