/** * Copyright (c) 2005-2017, KoLmafia development team * http://kolmafia.sourceforge.net/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * [1] Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * [2] Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * [3] Neither the name "KoLmafia" nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package net.sourceforge.kolmafia.request; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sourceforge.kolmafia.KoLCharacter; import net.sourceforge.kolmafia.KoLConstants.ZodiacType; import net.sourceforge.kolmafia.RequestLogger; import net.sourceforge.kolmafia.RequestThread; import net.sourceforge.kolmafia.persistence.AscensionSnapshot; import net.sourceforge.kolmafia.preferences.Preferences; import org.json.JSONException; import org.json.JSONObject; public class AccountRequest extends PasswordHashRequest { private static final Pattern SELECTED_PATTERN = Pattern.compile( "selected=\"selected\" value=\"?(\\d+)\"?>" ); public enum Tab { ALL( null ), INTERFACE( "interface" ), INVENTORY( "inventory" ), CHAT( "chat" ), COMBAT( "combat" ), ACCOUNT( "account" ), PROFILE( "profile" ), PRIVACY( "privacy" ), NONE( null ); private final String name; private Tab( String name ) { this.name = name; } @Override public String toString() { return this.name; } } private Tab tab; public AccountRequest() { this( Tab.ALL ); } public AccountRequest( final Tab tab ) { super( "account.php" ); this.tab = tab; String field = tab.toString(); if ( field != null ) { this.addFormField( "tab", field ); } } private static final Pattern TAB_PATTERN = Pattern.compile( "tab=([^&]*)" ); private static final Pattern LOADTAB_PATTERN = Pattern.compile( "action=loadtab&value=([^&]*)" ); private static final Tab getTab( final String urlString ) { if ( urlString.equals( "account.php" ) ) { return Tab.INTERFACE; } Matcher m = TAB_PATTERN.matcher( urlString ); if ( !m.find() ) { m = LOADTAB_PATTERN.matcher( urlString ); if ( !m.find() ) { return Tab.NONE; } } String tabName = m.group(1); for ( Tab tab : Tab.values() ) { if ( tabName.equals( tab.toString() ) ) { return tab; } } return Tab.NONE; } @Override protected boolean retryOnTimeout() { return true; } @Override public void run() { if ( this.tab == Tab.ALL ) { RequestThread.postRequest( new AccountRequest( Tab.INTERFACE ) ); RequestThread.postRequest( new AccountRequest( Tab.INVENTORY ) ); // RequestThread.postRequest( new AccountRequest ( Tab.CHAT ) ); RequestThread.postRequest( new AccountRequest( Tab.COMBAT ) ); RequestThread.postRequest( new AccountRequest( Tab.ACCOUNT ) ); // RequestThread.postRequest( new AccountRequest ( Tab.PROFILE ) ); // RequestThread.postRequest( new AccountRequest ( Tab.PRIVACY ) ); } else { super.run(); } } @Override public void processResults() { AccountRequest.parseAccountData( this.getURLString(), this.responseText ); } public static final void parseAccountData( final String location, final String responseText ) { if ( location.indexOf( "action=" ) != -1 ) { AccountRequest.parseAction( location, responseText ); return; } // Visiting a tab on the Options page PasswordHashRequest.updatePasswordHash( responseText ); AccountRequest.parseOptionTab( location, responseText ); } private static final void parseOptionTab( final String location, final String responseText ) { switch ( AccountRequest.getTab( location ) ) { case INTERFACE: AccountRequest.parseInterfaceOptions( responseText ); return; case INVENTORY: AccountRequest.parseInventoryOptions( responseText ); return; case CHAT: AccountRequest.parseChatOptions( responseText ); return; case COMBAT: AccountRequest.parseCombatOptions( responseText ); return; case ACCOUNT: AccountRequest.parseAccountOptions( responseText ); return; case PROFILE: AccountRequest.parseProfileOptions( responseText ); return; case PRIVACY: AccountRequest.parsePrivacyOptions( responseText ); return; } } private static boolean getCheckbox( final String flag, final String responseText ) { String test = "checked=\"checked\" name=\"" + flag + "\""; return responseText.indexOf( test ) != -1; } private static String fancyMenuStyle = "<input type=\"radio\" value=\"fancy\" checked=\"checked\" name=\"menu\"/>Icons"; private static String compactMenuStyle = "<input type=\"radio\" value=\"compact\" checked=\"checked\" name=\"menu\"/>Drop-Downs"; private static final void parseInterfaceOptions( final String responseText ) { // Top Menu Style GenericRequest.topMenuStyle = responseText.indexOf( fancyMenuStyle ) != -1 ? GenericRequest.MENU_FANCY : responseText.indexOf( compactMenuStyle ) != -1 ? GenericRequest.MENU_COMPACT : GenericRequest.MENU_NORMAL; boolean checked; checked = AccountRequest.getCheckbox( "flag_compactchar", responseText ); CharPaneRequest.compactCharacterPane = checked; checked = AccountRequest.getCheckbox( "flag_swapfam", responseText ); CharPaneRequest.familiarBelowEffects = checked; } private static final void parseInventoryOptions( final String responseText ) { boolean checked; checked = AccountRequest.getCheckbox( "flag_sellstuffugly", responseText ); KoLCharacter.setAutosellMode( checked ? "compact" : "detailed" ); checked = AccountRequest.getCheckbox( "flag_lazyinventory", responseText ); KoLCharacter.setLazyInventory( checked ); checked = AccountRequest.getCheckbox( "flag_unfamequip", responseText ); KoLCharacter.setUnequipFamiliar( checked ); } private static final void parseChatOptions( final String responseText ) { } private static final Pattern AUTOATTACK_PATTERN = Pattern.compile( "<select name=\"autoattack\">.*?</select>", Pattern.DOTALL ); private static final void parseCombatOptions( final String responseText ) { // Disable stationary buttons to avoid conflicts when // the action bar is enabled. boolean checked = AccountRequest.getCheckbox( "flag_wowbar", responseText ); Preferences.setBoolean( "serverAddsCustomCombat", checked ); int autoAttackAction = 0; Matcher selectMatcher = AccountRequest.AUTOATTACK_PATTERN.matcher( responseText ); if ( selectMatcher.find() ) { Matcher optionMatcher = AccountRequest.SELECTED_PATTERN.matcher( selectMatcher.group() ); if ( optionMatcher.find() ) { String autoAttackActionString = optionMatcher.group( 1 ); autoAttackAction = Integer.parseInt( autoAttackActionString ); } } KoLCharacter.setAutoAttackAction( autoAttackAction ); } private static final void parseAccountOptions( final String responseText ) { // Whether or not a player is currently in Bad Moon or hardcore // is also found here through the presence of buttons. // <input class=button name="action" type=submit value="Drop Hardcore"> boolean isHardcore = responseText.indexOf( "<input class=button name=\"action\" type=submit value=\"Drop Hardcore\">" ) != -1; KoLCharacter.setHardcore( isHardcore ); // <input class=button name="action" type=submit value="Drop Bad Moon"> if ( responseText.contains( "<input class=button name=\"action\" type=submit value=\"Drop Bad Moon\">" ) ) { KoLCharacter.setHardcore( true ); KoLCharacter.setSign( "Bad Moon" ); } else if ( KoLCharacter.getSignStat() == ZodiacType.BAD_MOON ) { KoLCharacter.setSign( "None" ); } // Your skills have been recalled if you have freed the king // and don't have a "Recall Skills" button in your account menu // <input class=button name="action" type="submit" value="Recall Skills"> boolean recalled = KoLCharacter.kingLiberated() && responseText.indexOf( "<input class=button name=\"action\" type=\"submit\" value=\"Recall Skills\">") == -1; KoLCharacter.setSkillsRecalled( recalled ); } private static final void parseProfileOptions( final String responseText ) { } private static final void parsePrivacyOptions( final String responseText ) { } private static final Pattern ACTION_PATTERN = Pattern.compile( "action=([^&]*)"); private static final Pattern VALUE_PATTERN = Pattern.compile( "value=([^&]*)"); private static final void parseAction( final String location, final String responseText ) { Matcher actionMatcher = AccountRequest.ACTION_PATTERN.matcher( location ); if ( !actionMatcher.find() ) { return; } String action = actionMatcher.group(1); if ( action.equals( "loadtab" ) ) { AccountRequest.parseOptionTab( location, responseText ); return; } Matcher valueMatcher = AccountRequest.VALUE_PATTERN.matcher( location ); String valueString = valueMatcher.find() ? valueMatcher.group(1) : ""; // Interface options if ( action.equals( "menu" ) ) { // account.php?pwd&action=menu&value=fancy&ajax=1 // account.php?pwd&action=menu&value=compact&ajax=1 // account.php?pwd&action=menu&value=normal&ajax=1 GenericRequest.topMenuStyle = valueString.equals( "fancy" ) ? GenericRequest.MENU_FANCY : valueString.equals( "compact" ) ? GenericRequest.MENU_COMPACT : GenericRequest.MENU_NORMAL; return; } if ( action.equals( "flag_compactchar" ) ) { boolean checked = valueString.equals( "1" ); CharPaneRequest.compactCharacterPane = checked; return; } if ( action.equals( "flag_swapfam" ) ) { boolean checked = valueString.equals( "1" ); CharPaneRequest.familiarBelowEffects = checked; return; } // Inventory options if ( action.equals( "flag_sellstuffugly" ) ) { boolean checked = valueString.equals( "1" ); KoLCharacter.setAutosellMode( checked ? "compact" : "detailed" ); return; } if ( action.equals( "flag_lazyinventory" ) ) { boolean checked = valueString.equals( "1" ); KoLCharacter.setLazyInventory( checked ); return; } if ( action.equals( "flag_unfamequip" ) ) { boolean checked = valueString.equals( "1" ); KoLCharacter.setUnequipFamiliar( checked ); return; } // Combat options if ( action.equals( "flag_wowbar" ) ) { boolean checked = valueString.equals( "1" ); Preferences.setBoolean( "serverAddsCustomCombat", checked ); return; } if ( action.equals( "autoattack" ) ) { int value = Integer.parseInt( valueString ); KoLCharacter.setAutoAttackAction( value ); return; } // Account options // <form method="post" action="account.php"> // account.php?actions[]=recallskills&action=Recall+Skills&tab=account&pwd // --> // You must confirm the confirmation box. // // account.php?actions[]=recallskills&action=Recall+Skills&recallconfirm=1&tab=account&pwd //--> // Your ancient memories return in a flood! You feel more // skilled! You remember some old familiar familiars! if ( action.equals( "Recall+Skills" ) ) { if ( location.contains( "recallconfirm=1" ) ) { // Recalling skills RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "Recalled ancient memories. Yowza!" ); RequestLogger.updateSessionLog(); KoLCharacter.setSkillsRecalled( true ); } return; } // Check for failure to drop path before checking to see if a path was dropped // For Boris, "You must abandon the Avatar of Boris before forsaking ronin." if ( responseText.contains( "You must abandon" ) ) { return; } // <input type=hidden name="actions[]" value="unronin"> // <input class=button name="action" type=submit value="Forsake Ronin"> // <input type="checkbox" class="confirm" name="unroninconfirm" value="1"> // account.php?actions[]=unronin&action=Forsake+Ronin&unroninconfirm=1&tab=account&pwd if ( action.equals( "Forsake+Ronin" ) ) { if ( location.contains( "unroninconfirm=1" ) ) { // Dropping from Softcore to Casual. KoLCharacter.setRonin( false ); CharPaneRequest.setInteraction( true ); RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "Dropped into Casual. Slacker." ); RequestLogger.updateSessionLog(); } return; } // <input type=hidden name="actions[]" value="unpath"> // <input class=button name="action" type="submit" value="Drop Oxygenarian"> // <input type="checkbox" class="confirm" name="unpathconfirm" value="1"> // account.php?actions[]=unpath&action=Drop+Teetotaler&unpathconfirm=1&tab=account&pwd // account.php?actions[]=unpath&action=Drop+Boozetafarian&unpathconfirm=1&tab=account&pwd // account.php?actions[]=unpath&action=Drop+Oxygenarian&unpathconfirm=1&tab=account&pwd if ( action.equals( "Drop+Teetotaler" ) || action.equals( "Drop+Boozetafarian" ) || action.equals( "Drop+Oxygenarian" ) ) { if ( location.contains( "unpathconfirm=1" ) ) { // Dropping consumption restrictions KoLCharacter.setConsumptionRestriction( AscensionSnapshot.NOPATH ); RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "Dropped consumption restrictions." ); RequestLogger.updateSessionLog(); } return; } // <input type=hidden name="actions[]" value="unhardcore"> // <input class=button name="action" type=submit value="Drop Hardcore"> // <input type="checkbox" class="confirm" name="unhardcoreconfirm" value="1"> // account.php?actions[]=unhardcore&action=Drop+Hardcore&unhardcoreconfirm=1&tab=account&pwd if ( action.equals( "Drop+Hardcore" ) ) { if ( location.indexOf( "unhardcoreconfirm=1" ) != -1 ) { // Dropping Hardcore KoLCharacter.setHardcore( false ); KoLCharacter.setRonin( KoLCharacter.getCurrentRun() < 1000 ); CharPaneRequest.setInteraction( KoLCharacter.getCurrentRun() >= 1000 ); RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "Dropped Hardcore. Wimp." ); RequestLogger.updateSessionLog(); } return; } // <input type=hidden name="actions[]" value="unbadmoon"> // <input class=button name="action" type=submit value="Drop Bad Moon"> // <input type="checkbox" class="confirm" name="unbadmoonconfirm" value="1"> // account.php?actions[]=unbadmoon&action=Drop+Bad+Moon&unbadmoonconfirm=1&tab=account&pwd if ( action.equals( "Drop+Bad+Moon" ) ) { if ( location.indexOf( "unbadmoonconfirm=1" ) != -1 ) { // Dropping Bad Moon KoLCharacter.setSign( "None" ); RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "Dropped Bad Moon. You fool!" ); RequestLogger.updateSessionLog(); } return; } // Anything not covered above is dropping a challenge path // account.php?actions[]=unpath&action=Drop+Bees+Hate+You&unpathconfirm=1&tab=account&pwd // account.php?actions[]=unpath&action=Drop+Way+of+the+Surprising+Fist&unpathconfirm=1&tab=account&pwd // account.php?actions[]=unpath&action=Drop+Trendy&unpathconfirm=1&tab=account&pwd // account.php?actions[]=unpath&action=Drop+Avatar+of+Boris&unpathconfirm=1&tab=account&pwd // account.php?actions[]=unpath&action=Drop+Bugbear+Invasion&unpathconfirm=1&tab=account&pwd if ( action.startsWith( "Drop+" ) ) { if ( location.contains( "unpathconfirm=1" ) ) { // Dropping challenge path String oldPath = KoLCharacter.getPath(); KoLCharacter.setPath( "None" ); RequestLogger.updateSessionLog(); RequestLogger.updateSessionLog( "Dropped " + oldPath ); RequestLogger.updateSessionLog(); // If we were in Beecore, we need to check the Telescope again // Ditto for Bugbear Invasion if ( oldPath.equals( "Bees Hate You" ) || oldPath.equals( "Bugbear Invasion" ) ) { Preferences.setInteger( "lastTelescopeReset", -1 ); KoLCharacter.checkTelescope(); } // If we drop Avatar of Boris, do we get here, // or are we redirected to the "End of the // Boris Road" choice adventure? } return; } } public static final void parseStatus( final JSONObject JSON ) throws JSONException { JSONObject flags = JSON.getJSONObject( "flag_config" ); boolean checked; // Interface options /* // This is currently busted int topmenu = flags.getInt( "topmenu" ); GenericRequest.topMenuStyle = (topmenu == 2 ) ? GenericRequest.MENU_FANCY : (topmenu == 1 ) ? GenericRequest.MENU_COMPACT : GenericRequest.MENU_NORMAL; */ checked = flags.getInt( "compactchar" ) == 1; CharPaneRequest.compactCharacterPane = checked; checked = flags.getInt( "swapfam" ) == 1; CharPaneRequest.familiarBelowEffects = checked; checked = flags.getInt( "ignorezonewarnings" ) == 1; KoLCharacter.setIgnoreZoneWarnings( checked ); // Inventory options checked = flags.getInt( "sellstuffugly" ) == 1; KoLCharacter.setAutosellMode( checked ? "compact" : "detailed" ); checked = flags.getInt( "lazyinventory" ) == 1; KoLCharacter.setLazyInventory( checked ); checked = flags.getInt( "unfamequip" ) == 1; KoLCharacter.setUnequipFamiliar( checked ); // Combat options checked = flags.getInt( "wowbar" ) == 1; Preferences.setBoolean( "serverAddsCustomCombat", checked ); int autoAttackAction = flags.getInt( "autoattack" ); KoLCharacter.setAutoAttackAction( autoAttackAction ); // Account options String sign = JSON.getString( "sign" ); KoLCharacter.setSign( sign ); String path = JSON.getString( "path" ); if ( path.equals( "0" ) ) { path = "None"; } else if ( path.equals( "1" ) ) { path = "Boozetafarian"; } else if ( path.equals( "2" ) ) { path = "Teetotaler"; } else if ( path.equals( "3" ) ) { path = "Oxygenarian"; } else if ( path.equals( "4" ) ) { path = "Bees Hate You"; } else if ( path.equals( "6" ) ) { path = "Way of the Surprising Fist"; } else if ( path.equals( "7" ) ) { path = "Trendy"; } else if ( path.equals( "8" ) ) { path = "Avatar of Boris"; } else if ( path.equals( "9" ) ) { path = "Bugbear Invasion"; } else if ( path.equals( "10" ) ) { path = "Zombie Slayer"; } else if ( path.equals( "11" ) ) { path = "Class Act"; } else if ( path.equals( "12" ) ) { path = "Avatar of Jarlsberg"; } else if ( path.equals( "14" ) ) { path = "BIG!"; } else if ( path.equals( "15" ) ) { path = "KOLHS"; } else if ( path.equals( "16" ) ) { path = "Class Act II: A Class For Pigs"; } else if ( path.equals( "17" ) ) { path = "Avatar of Sneaky Pete"; } else if ( path.equals( "18" ) ) { path = "Slow and Steady"; } else if ( path.equals( "19" ) ) { path = "Heavy Rains"; } else if ( path.equals( "21" ) ) { path = "Picky"; } else if ( path.equals( "22" ) ) { path = "Standard"; } else if ( path.equals( "23" ) ) { path = "Actually Ed the Undying"; } else if ( path.equals( "24" ) ) { path = "One Crazy Random Summer"; } else if ( path.equals( "25" ) ) { path = "Community Service"; } else if ( path.equals( "26" ) ) { path = "Avatar of West of Loathing"; } else if ( path.equals( "27" ) ) { path = "The Source"; } else if ( path.equals( "28" ) ) { path = "Nuclear Autumn"; } else if ( path.equals( "29" ) ) { path = "Gelatinous Noob"; } KoLCharacter.setPath( path ); boolean hardcore = JSON.getInt( "hardcore" ) == 1 || sign.equals( "Bad Moon" ); KoLCharacter.setHardcore( hardcore ); // This isn't safe in Ed after defeating adventurer, but if we're Ed we haven't freed ralph! if ( !path.equals( "Actually Ed the Undying" ) ) { boolean liberated = JSON.getInt( "freedralph" ) == 1; KoLCharacter.setKingLiberated( liberated ); } else { KoLCharacter.setKingLiberated( false ); } boolean recalled = JSON.getInt( "recalledskills" ) == 1; KoLCharacter.setSkillsRecalled( recalled ); int eudora = flags.getInt( "whichpenpal" ); switch ( eudora ) { case 0: KoLCharacter.setEudora( "none" ); break; case 1: KoLCharacter.setEudora( "Penpal" ); break; case 2: KoLCharacter.setEudora( "GameInformPowerDailyPro Magazine" ); break; case 3: KoLCharacter.setEudora( "Xi Receiver Unit" ); break; case 4: KoLCharacter.setEudora( "New-You Club" ); break; default: KoLCharacter.setEudora( "Unknown" ); break; } } }