package me.corriekay.pokegoutil.data.managers; import java.awt.BorderLayout; import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; import javax.swing.UIManager; import com.pokegoapi.api.PokemonGo; import com.pokegoapi.api.player.PlayerProfile; import com.pokegoapi.auth.CredentialProvider; import com.pokegoapi.auth.GoogleAutoCredentialProvider; import com.pokegoapi.auth.GoogleUserCredentialProvider; import com.pokegoapi.auth.PtcCredentialProvider; import com.pokegoapi.exceptions.AsyncPokemonGoException; import com.pokegoapi.exceptions.CaptchaActiveException; import com.pokegoapi.exceptions.LoginFailedException; import com.pokegoapi.exceptions.hash.HashException; import me.corriekay.pokegoutil.data.enums.LoginType; import me.corriekay.pokegoutil.utils.ConfigKey; import me.corriekay.pokegoutil.utils.ConfigNew; import me.corriekay.pokegoutil.utils.StringLiterals; import me.corriekay.pokegoutil.utils.helpers.Browser; import me.corriekay.pokegoutil.utils.helpers.LoginHelper; import me.corriekay.pokegoutil.utils.windows.WindowStuffHelper; import me.corriekay.pokegoutil.windows.PokemonGoMainWindow; import okhttp3.OkHttpClient; /** * This controller does the login/log off, and different account information (aka player data). */ @Deprecated public final class AccountController { private static final AccountController instance = new AccountController(); private static boolean sIsInit = false; private static ConfigNew config = ConfigNew.getConfig(); protected PokemonGoMainWindow mainWindow = null; protected PokemonGo go = null; protected OkHttpClient http; protected CredentialProvider cp; private boolean logged = false; private AccountController() { } public static AccountController getInstance() { return instance; } /** * Does pre-initializiton before using of the class. */ public static void initialize() { if (sIsInit) { return; } sIsInit = true; } public static void logOn() { if (!sIsInit) { throw new ExceptionInInitializerError("AccountController needs to be initialized before logging on"); } OkHttpClient http; CredentialProvider credentialProvider; PokemonGo go = null; int tries = 0; while (!instance.logged) { tries++; //BEGIN LOGIN WINDOW go = null; credentialProvider = null; http = new OkHttpClient(); final JTextField ptcUsernameTextField = new JTextField(config.getString(ConfigKey.LOGIN_PTC_USERNAME)); final JTextField ptcPasswordTextField = new JPasswordField(config.getString(ConfigKey.LOGIN_PTC_PASSWORD)); final int directLoginWithSavedCredentials = checkForSavedCredentials(); int response; if (directLoginWithSavedCredentials == JOptionPane.CANCEL_OPTION) { System.exit(0); return; } else if (directLoginWithSavedCredentials == JOptionPane.YES_OPTION) { if (getLoginData(LoginType.GOOGLE_AUTH) != null || getLoginData(LoginType.GOOGLE_APP_PASSWORD) != null) { response = JOptionPane.NO_OPTION; // This means Google. Trust me } else if (getLoginData(LoginType.PTC) != null) { response = JOptionPane.YES_OPTION; // And this PTC. Yeah, really } else { // Something is wrong here, we delete login and start anew deleteLoginData(LoginType.ALL); return; } } else { // We do not want to login directly, so go for the question box and delete that data before deleteLoginData(LoginType.ALL); UIManager.put("OptionPane.noButtonText", "Use Google Auth"); UIManager.put("OptionPane.yesButtonText", "Use PTC Auth"); UIManager.put("OptionPane.cancelButtonText", "Exit"); UIManager.put("OptionPane.okButtonText", "Ok"); final JPanel panel1 = new JPanel(new BorderLayout()); panel1.add(new JLabel("PTC Username: "), BorderLayout.LINE_START); panel1.add(ptcUsernameTextField, BorderLayout.CENTER); final JPanel panel2 = new JPanel(new BorderLayout()); panel2.add(new JLabel("PTC Password: "), BorderLayout.LINE_START); panel2.add(ptcPasswordTextField, BorderLayout.CENTER); final Object[] panel = {panel1, panel2}; response = JOptionPane.showConfirmDialog( WindowStuffHelper.ALWAYS_ON_TOP_PARENT, panel, "Login", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); } if (response == JOptionPane.CANCEL_OPTION || response == JOptionPane.CLOSED_OPTION) { System.exit(0); } else if (response == JOptionPane.OK_OPTION) { try { credentialProvider = new PtcCredentialProvider(http, ptcUsernameTextField.getText(), ptcPasswordTextField.getText()); config.setString(ConfigKey.LOGIN_PTC_USERNAME, ptcUsernameTextField.getText()); if (config.getBool(ConfigKey.LOGIN_SAVE_AUTH) || checkSaveAuth()) { config.setString(ConfigKey.LOGIN_PTC_PASSWORD, ptcPasswordTextField.getText()); config.setBool(ConfigKey.LOGIN_SAVE_AUTH, true); } else { deleteLoginData(LoginType.PTC); } } catch (final Exception e) { alertFailedLogin(e.getClass().getSimpleName(), e.getMessage(), tries); e.printStackTrace(); // deleteLoginData(LoginType.PTC); continue; } //Using PTC, remove Google infos deleteLoginData(LoginType.GOOGLE_AUTH, true); deleteLoginData(LoginType.GOOGLE_APP_PASSWORD, true); } else if (response == JOptionPane.NO_OPTION) { final String googleAuthTitle = "Google Auth"; // We to set up some vars that we may need later. // Those will be overwritten if data is entered, otherwise they should contain the correct values loaded from the config. String googleAuthToken = config.getString(ConfigKey.LOGIN_GOOGLE_AUTH_TOKEN, null); String googleUsername = config.getString(ConfigKey.LOGIN_GOOGLE_APP_USERNAME, null); String googlePassword = config.getString(ConfigKey.LOGIN_GOOGLE_APP_PASSWORD, null); boolean authTokenRefresh = false; LoginType usedGoogleLoginType = checkSavedConfig(); if (LoginType.isGoogle(usedGoogleLoginType)) { // We check if google login data saved and skip the input options then if (usedGoogleLoginType == LoginType.GOOGLE_AUTH) { // Sets refresh, when we use the existing token, cause it needs to be refreshed authTokenRefresh = true; } } else { // Okay, user should choose which login method he wants: String message = "Choose your method to login with your Google Account." + StringLiterals.NEWLINE + StringLiterals.NEWLINE + "If you are unexperienced, just choose \"OAuth Token\"." + StringLiterals.NEWLINE + "If you have trouble logging in with that method, or know what you are doing," + StringLiterals.NEWLINE + "go ahead and take the App Password method."; String[] options = new String[] {"Use OAuth Token", "Advanced: Use App Password"}; final int answer = JOptionPane.showOptionDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, message, googleAuthTitle, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); switch (answer) { case 0: // Use OAuth Token //We need to get the auth code, as we do not have it yet. UIManager.put("OptionPane.okButtonText", "Ok"); JOptionPane.showMessageDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, "You will need to provide a google authentication key to log in." + StringLiterals.NEWLINE + "A webpage should open up, please allow the permissions, and then copy the code into your clipboard." + StringLiterals.NEWLINE + StringLiterals.NEWLINE + "Press OK to continue", googleAuthTitle, JOptionPane.PLAIN_MESSAGE); //We're gonna try to load the page using the users browser final boolean success = Browser.openUrl(GoogleUserCredentialProvider.LOGIN_URL); // Okay, couldn't open it. We use the manual copy window UIManager.put("OptionPane.cancelButtonText", "Copy To Clipboard"); if (!success && JOptionPane.showConfirmDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, "Please copy this link and paste it into a browser.\nThen, allow the permissions, and copy the code into your clipboard.", googleAuthTitle, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.CANCEL_OPTION) { final StringSelection ss = new StringSelection(GoogleUserCredentialProvider.LOGIN_URL); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, ss); } UIManager.put("OptionPane.cancelButtonText", ButtonText.CANCEL); //The user should have the auth code now. Lets get it. googleAuthToken = JOptionPane.showInputDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, "Please provide the authentication code", googleAuthTitle, JOptionPane.PLAIN_MESSAGE); usedGoogleLoginType = LoginType.GOOGLE_AUTH; break; case 1: // Advanced: Use App Password //We need to get the user data, as we do not have it yet. final String appPasswordMessage = "You want to login via App Password." + StringLiterals.NEWLINE + "For that, an app password has to be created." + StringLiterals.NEWLINE + "If you already have your password, click \"Skip\"." + StringLiterals.NEWLINE + StringLiterals.NEWLINE + "Otherwise, click on \"Open Website\" to access the google account control page where you are able" + StringLiterals.NEWLINE + "to create an app password." + StringLiterals.NEWLINE + "Choose 'Other' as app and name it something like 'BPGM' or such. Then copy your password." + StringLiterals.NEWLINE + StringLiterals.NEWLINE + "Do note: If google tells you you can't configure your App Passwords, then go back to your" + StringLiterals.NEWLINE + "google account control page and enable 2-Step Verification. Then you will be able to."; final String[] appPasswordOptions = new String[] {"Open Website", "Skip"}; final int appPasswordResponse = JOptionPane.showOptionDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, appPasswordMessage, googleAuthTitle, JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, appPasswordOptions, options[0]); // Open URL if chosen switch (appPasswordResponse) { case 0: // Open Webpage String appPasswordUrl = "https://security.google.com/settings/security/apppasswords"; Browser.openUrl(appPasswordUrl); break; case 1: // Skip // We do nothing here, we have skipped break; default: // Can't happen } // We ask the user to enter his login data now final int width = 15; JTextField googleUsernameTextField = new JTextField(width); JTextField googlePasswordTextField = new JTextField(width); final JPanel panel1 = new JPanel(new BorderLayout()); panel1.add(new JLabel("Username: "), BorderLayout.LINE_START); panel1.add(googleUsernameTextField, BorderLayout.CENTER); final JPanel panel2 = new JPanel(new BorderLayout()); panel2.add(new JLabel("Password: "), BorderLayout.LINE_START); panel2.add(googlePasswordTextField, BorderLayout.CENTER); final Object[] goggleAppPasswordPanel = {panel1, panel2}; final int googleAppLoginDetailsResult = JOptionPane.showConfirmDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, goggleAppPasswordPanel, googleAuthTitle, JOptionPane.OK_CANCEL_OPTION); if (googleAppLoginDetailsResult == JOptionPane.OK_OPTION) { googleUsername = googleUsernameTextField.getText(); googlePassword = googlePasswordTextField.getText(); } usedGoogleLoginType = LoginType.GOOGLE_APP_PASSWORD; break; default: // Can't happen } } // Okay, we gathered all information now, so we will decide what we do now with it // and then log in. try { GoogleUserCredentialProvider googleProvider = null; GoogleAutoCredentialProvider googleAutoProvider; if (usedGoogleLoginType == LoginType.GOOGLE_AUTH) { // We have google OAuth here, so we use the token and/or refresh it if (authTokenRefresh) { // Based on usage in https://github.com/Grover-c13/PokeGOAPI-Java googleProvider = new GoogleUserCredentialProvider(http, googleAuthToken); } else { googleProvider = new GoogleUserCredentialProvider(http); googleProvider.login(googleAuthToken); } credentialProvider = googleProvider; } else if (usedGoogleLoginType == LoginType.GOOGLE_APP_PASSWORD) { // We have app password login here googleAutoProvider = new GoogleAutoCredentialProvider(http, googleUsername, googlePassword); credentialProvider = googleAutoProvider; } // Now check if the data should be saved in config. Asking the user. if (config.getBool(ConfigKey.LOGIN_SAVE_AUTH) || checkSaveAuth()) { // We save that auth is generally saved config.setBool(ConfigKey.LOGIN_SAVE_AUTH, true); // We save the login data now if (usedGoogleLoginType == LoginType.GOOGLE_AUTH && !authTokenRefresh) { config.setString(ConfigKey.LOGIN_GOOGLE_AUTH_TOKEN, googleProvider.getRefreshToken()); } else if (usedGoogleLoginType == LoginType.GOOGLE_APP_PASSWORD) { config.setString(ConfigKey.LOGIN_GOOGLE_APP_USERNAME, googleUsername); config.setString(ConfigKey.LOGIN_GOOGLE_APP_PASSWORD, googlePassword); } } else { deleteLoginData(LoginType.GOOGLE_APP_PASSWORD); deleteLoginData(LoginType.GOOGLE_AUTH); } } catch (final Exception e) { alertFailedLogin(e.getClass().getSimpleName(), e.getMessage(), tries); e.printStackTrace(); // deleteLoginData(LoginType.GOOGLE_APP_PASSWORD); // deleteLoginData(LoginType.GOOGLE_AUTH); continue; } //Using Google, remove PTC infos deleteLoginData(LoginType.PTC, true); } UIManager.put("OptionPane.noButtonText", "No"); UIManager.put("OptionPane.yesButtonText", "Yes"); UIManager.put("OptionPane.okButtonText", "Ok"); UIManager.put("OptionPane.cancelButtonText", ButtonText.CANCEL); try { if (credentialProvider != null) { go = new PokemonGo(http); LoginHelper.login(go, credentialProvider, api -> { instance.logged = true; instance.go = api; initOtherControllers(api); instance.mainWindow = new PokemonGoMainWindow(api, true); instance.mainWindow.start(); }); } else { throw new IllegalStateException("credentialProvider is null."); } } catch (LoginFailedException | AsyncPokemonGoException | IllegalStateException | CaptchaActiveException | HashException e) { if (e instanceof AsyncPokemonGoException && e.getCause() instanceof RuntimeException && e.getCause().getCause() instanceof ExecutionException && e.getCause().getCause().getCause() instanceof HashException) { alertFailedLogin(e.getCause().getCause().getCause().getClass().getSimpleName(), e.getCause().getCause().getCause().getMessage(), tries); } else { alertFailedLogin(e.getClass().getSimpleName(), e.getMessage(), tries); } e.printStackTrace(); // deleteLoginData(LoginType.ALL); } } } private static void initOtherControllers(final PokemonGo go) { InventoryManager.initialize(go); PokemonBagManager.initialize(go); } /** * Alerts a failed login with a popup showing the specific error. * * @param exceptionClass The class of the exception, or error type. * @param message The message that is shown. * @param tries The number of the try that this is (zero based). */ private static void alertFailedLogin(final String exceptionClass, final String message, final int tries) { System.out.println("Error: " + exceptionClass + StringLiterals.NEWLINE + message); JOptionPane.showMessageDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, "Unfortunately, your login has failed. Reason: " + StringLiterals.NEWLINE + exceptionClass + ": " + message + StringLiterals.NEWLINE + "This is try number " + tries + "." + StringLiterals.NEWLINE + "Press OK to try again.", "Login Failed", JOptionPane.ERROR_MESSAGE); } private static boolean checkSaveAuth() { UIManager.put("OptionPane.noButtonText", "No"); UIManager.put("OptionPane.yesButtonText", "Yes"); UIManager.put("OptionPane.okButtonText", "Ok"); UIManager.put("OptionPane.cancelButtonText", ButtonText.CANCEL); return JOptionPane.showConfirmDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, "Do you wish to save the password/auth token?\nCaution: These are saved in plain-text.", "Save Authentication?", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION; } private static LoginType checkSavedConfig() { if (!config.getBool(ConfigKey.LOGIN_SAVE_AUTH)) { return LoginType.NONE; } else { if (getLoginData(LoginType.GOOGLE_AUTH) != null) { return LoginType.GOOGLE_AUTH; } if (getLoginData(LoginType.GOOGLE_APP_PASSWORD) != null) { return LoginType.GOOGLE_APP_PASSWORD; } if (getLoginData(LoginType.PTC) != null) { return LoginType.PTC; } return LoginType.NONE; } } private static List<String> getLoginData(final LoginType type) { switch (type) { case GOOGLE_AUTH: final String token = config.getString(ConfigKey.LOGIN_GOOGLE_AUTH_TOKEN); return (token != null) ? Collections.singletonList(token) : null; case GOOGLE_APP_PASSWORD: final String googleUsername = config.getString(ConfigKey.LOGIN_GOOGLE_APP_USERNAME); final String googlePassword = config.getString(ConfigKey.LOGIN_GOOGLE_APP_PASSWORD); return (googleUsername != null && googlePassword != null) ? Arrays.asList(googleUsername, googlePassword) : null; case PTC: final String ptcUsername = config.getString(ConfigKey.LOGIN_PTC_USERNAME); final String ptcPassword = config.getString(ConfigKey.LOGIN_PTC_PASSWORD); return (ptcUsername != null && ptcPassword != null) ? Arrays.asList(ptcUsername, ptcPassword) : null; default: return null; } } private static void deleteLoginData(final LoginType type) { deleteLoginData(type, false); } private static void deleteLoginData(final LoginType type, final boolean justCleanup) { if (!justCleanup) { config.delete(ConfigKey.LOGIN_SAVE_AUTH); } switch (type) { case ALL: config.delete(ConfigKey.LOGIN_GOOGLE_AUTH_TOKEN); config.delete(ConfigKey.LOGIN_GOOGLE_APP_USERNAME); config.delete(ConfigKey.LOGIN_GOOGLE_APP_PASSWORD); config.delete(ConfigKey.LOGIN_PTC_USERNAME); config.delete(ConfigKey.LOGIN_PTC_PASSWORD); break; case PTC: config.delete(ConfigKey.LOGIN_PTC_USERNAME); config.delete(ConfigKey.LOGIN_PTC_PASSWORD); break; case GOOGLE_AUTH: config.delete(ConfigKey.LOGIN_GOOGLE_AUTH_TOKEN); break; case GOOGLE_APP_PASSWORD: config.delete(ConfigKey.LOGIN_GOOGLE_APP_USERNAME); config.delete(ConfigKey.LOGIN_GOOGLE_APP_PASSWORD); break; default: } } /** * Check if there is any login saved and ask for user to use it or not. * @return JOptionPane.NO_OPTION, JOptionPane.YES_OPTION, JOptionPane.CANCEL_OPTION */ private static int checkForSavedCredentials() { final LoginType savedLogin = checkSavedConfig(); if (savedLogin == LoginType.NONE) { return JOptionPane.NO_OPTION; } else { UIManager.put("OptionPane.noButtonText", "No"); UIManager.put("OptionPane.yesButtonText", "Yes"); UIManager.put("OptionPane.okButtonText", "Ok"); UIManager.put("OptionPane.cancelButtonText", "Exit"); return JOptionPane.showConfirmDialog(WindowStuffHelper.ALWAYS_ON_TOP_PARENT, "You have saved login data for " + savedLogin.toString() + ". Want to login with that?", "Use Saved Login", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); } } public static void logOff() throws Exception { if (!sIsInit) { throw new ExceptionInInitializerError("AccountController needs to be initialized before logging on"); } if (!instance.logged) { return; } instance.logged = false; instance.mainWindow.setVisible(false); instance.mainWindow.dispose(); instance.mainWindow = null; logOn(); } public static PlayerProfile getPlayerProfile() { return instance.go != null ? instance.go.getPlayerProfile() : null; } /** * Inner class that saves the texts buttons can contain. */ private static final class ButtonText { private static final String CANCEL = "Cancel"; } }