/**
* 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;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.request.RelayRequest;
import net.sourceforge.kolmafia.textui.Interpreter;
import net.sourceforge.kolmafia.textui.NamespaceInterpreter;
import net.sourceforge.kolmafia.textui.RuntimeLibrary;
import net.sourceforge.kolmafia.textui.parsetree.Function;
import net.sourceforge.kolmafia.textui.parsetree.FunctionList;
import net.sourceforge.kolmafia.textui.parsetree.VariableReference;
public abstract class KoLmafiaASH
{
private static final HashMap<String, File> relayScriptMap = new HashMap<String, File>();
private static final HashMap<File, Long> TIMESTAMPS = new HashMap<File, Long>();
private static final HashMap<File, Interpreter> INTERPRETERS = new HashMap<File, Interpreter>();
public static final Interpreter NAMESPACE_INTERPRETER = new NamespaceInterpreter();
public static final void logScriptExecution( final String prefix, final String scriptName, Interpreter script )
{
KoLmafiaASH.logScriptExecution( prefix, scriptName, "", script );
}
public static final void logScriptExecution( final String prefix, final String scriptName, final String postfix, Interpreter script )
{
boolean isDebugging = RequestLogger.isDebugging();
boolean isTracing = RequestLogger.isTracing();
boolean scriptIsTracing = Interpreter.isTracing();
if ( !isDebugging && !isTracing && !scriptIsTracing )
{
return;
}
String message = prefix + scriptName + postfix;
if ( isDebugging )
{
RequestLogger.updateDebugLog( message );
}
if ( isTracing )
{
RequestLogger.trace( message );
}
if ( scriptIsTracing )
{
script.trace( message );
}
}
public static final boolean getClientHTML( final RelayRequest request )
{
String script = Preferences.getString( "masterRelayOverride" );
String field = null;
if ( script.length() == 0 || request.getPath().startsWith( "relay_" ) )
{
script = request.getBasePath();
if ( script.equals( "place.php" ) )
{
field = request.getFormField( "whichplace" );
}
else if ( script.equals( "shop.php" ) )
{
field = request.getFormField( "whichshop" );
}
}
if ( field != null )
{
String fullscript = script.substring( 0, script.length() - 4 ) + "." + field + ".ash";
File toExecute;
if ( KoLmafiaASH.relayScriptMap.containsKey( fullscript ) )
{
toExecute = KoLmafiaASH.relayScriptMap.get( fullscript );
}
else
{
toExecute = new File( KoLConstants.RELAY_LOCATION, fullscript );
KoLmafiaASH.relayScriptMap.put( fullscript, toExecute );
}
if ( toExecute.exists() )
{
return KoLmafiaASH.getClientHTML( request, toExecute );
}
}
if ( KoLmafiaASH.relayScriptMap.containsKey( script ) )
{
File toExecute = KoLmafiaASH.relayScriptMap.get( script );
return toExecute.exists() && KoLmafiaASH.getClientHTML( request, toExecute );
}
if ( !script.endsWith( ".ash" ) )
{
if ( !script.endsWith( ".php" ) )
{
return false;
}
script = script.substring( 0, script.length() - 4 ) + ".ash";
}
File toExecute = new File( KoLConstants.RELAY_LOCATION, script );
KoLmafiaASH.relayScriptMap.put( script, toExecute );
return toExecute.exists() && KoLmafiaASH.getClientHTML( request, toExecute );
}
private static final boolean getClientHTML( final RelayRequest request, final File toExecute )
{
Interpreter relayScript = KoLmafiaASH.getInterpreter( toExecute );
if ( relayScript == null )
{
return false;
}
synchronized ( relayScript )
{
// We are synchronized, so no other thread is in this
// relay script, but this thread could be inside it: if
// KoL redirects to the same page (but with different
// arguments), the same script will want to handle the
// redirection.
if ( relayScript.getRelayRequest() != null )
{
return false;
}
KoLmafiaASH.logScriptExecution( "Starting relay script: ", toExecute.getName(), "", relayScript );
RelayRequest relayRequest = new RelayRequest( false );
relayRequest.cloneURLString( request );
relayScript.initializeRelayScript( relayRequest );
relayScript.execute( "main", null );
StringBuffer serverReplyBuffer = relayScript.getServerReplyBuffer();
if ( serverReplyBuffer.length() == 0 )
{
if ( relayRequest.responseText != null && relayRequest.responseText.length() != 0 )
{
serverReplyBuffer.append( relayRequest.responseText );
}
}
int written = serverReplyBuffer.length();
if ( written != 0 )
{
String response = serverReplyBuffer.toString();
request.pseudoResponse( "HTTP/1.1 200 OK", response );
}
relayScript.finishRelayScript();
KoLmafiaASH.logScriptExecution( "Finished relay script: ", toExecute.getName(), " (" + written + " bytes)", relayScript );
return written != 0;
}
}
public static final String getScriptHTML( final Interpreter relayScript, final String serverFunc, final String path )
{
synchronized( relayScript )
{
RelayRequest relayRequest = new RelayRequest( false );
relayRequest.constructURLString( path );
relayScript.initializeRelayScript( relayRequest );
String script = Preferences.getString( "masterRelayOverride" );
if ( script.length() == 0 || relayRequest.getPath().startsWith( "relay_" ) )
{
script = relayRequest.getBasePath();
}
int slashpos = script.lastIndexOf( "/" );
relayScript.execute( serverFunc,
new String[] { script.substring( slashpos + 1 ) },
false );
StringBuffer serverReplyBuffer = relayScript.getServerReplyBuffer();
if ( serverReplyBuffer.length() == 0 )
{
serverReplyBuffer.append( "<html><body>Script failed to write any output!</body></html>" );
}
String response = serverReplyBuffer.toString();
relayScript.finishRelayScript();
return response;
}
}
// Convenience method so that callers can just do getInterpreter( KoLMafiaCLI.findScriptFile() )
public static Interpreter getInterpreter( List<File> findScriptFile )
{
if ( findScriptFile.size() > 1 )
{
RequestLogger.printList( findScriptFile );
RequestLogger.printLine( "Multiple matching scripts in your current namespace." );
return null;
}
if ( findScriptFile.size() == 1 )
return getInterpreter( findScriptFile.get( 0 ) );
return null;
}
public static final Interpreter getInterpreter( final File toExecute )
{
if ( toExecute == null )
{
return null;
}
boolean createInterpreter = !KoLmafiaASH.TIMESTAMPS.containsKey( toExecute );
if ( !createInterpreter )
{
Long timestamp = KoLmafiaASH.TIMESTAMPS.get( toExecute );
createInterpreter = timestamp != toExecute.lastModified();
}
if ( !createInterpreter )
{
Interpreter interpreter = (Interpreter) KoLmafiaASH.INTERPRETERS.get( toExecute );
TreeMap imports = interpreter.getImports();
Iterator it = imports.entrySet().iterator();
while ( it.hasNext() && !createInterpreter )
{
Entry entry = (Entry) it.next();
File file = (File) entry.getKey();
Long timestamp = (Long) entry.getValue();
createInterpreter = timestamp != file.lastModified();
}
}
if ( createInterpreter )
{
KoLmafiaASH.TIMESTAMPS.remove( toExecute );
Interpreter interpreter = new Interpreter();
if ( !interpreter.validate( toExecute, null ) )
{
return null;
}
KoLmafiaASH.TIMESTAMPS.put( toExecute, toExecute.lastModified() );
KoLmafiaASH.INTERPRETERS.put( toExecute, interpreter );
}
return KoLmafiaASH.INTERPRETERS.get( toExecute );
}
public static void showUserFunctions( final Interpreter interpreter, final String filter )
{
KoLmafiaASH.showFunctions( interpreter.getFunctions(), filter.toLowerCase(), false );
}
public static void showExistingFunctions( final String filter )
{
KoLmafiaASH.showFunctions( RuntimeLibrary.getFunctions(), filter.toLowerCase(), true );
}
private static void showFunctions( final FunctionList functions, final String filter, boolean addLinks )
{
addLinks = addLinks && StaticEntity.isGUIRequired();
if ( functions.isEmpty() )
{
RequestLogger.printLine( "No functions in your current namespace." );
return;
}
for ( Function func : functions )
{
boolean matches = filter.equals( "" );
if ( !matches )
{
matches = func.getName().toLowerCase().contains( filter );
}
if ( !matches )
{
for ( VariableReference ref : func.getVariableReferences() )
{
String refType = ref.getType().toString();
matches = refType != null && refType.contains( filter );
}
}
if ( !matches )
{
continue;
}
StringBuilder description = new StringBuilder();
description.append( func.getType() );
description.append( " " );
if ( addLinks )
{
description.append( "<a href='http://wiki.kolmafia.us/index.php?title=" );
description.append( func.getName() );
description.append( "'>" );
}
description.append( func.getName() );
if ( addLinks )
{
description.append( "</a>" );
}
description.append( "( " );
String sep = "";
for ( VariableReference var : func.getVariableReferences() )
{
description.append( sep );
sep = ", ";
description.append( var.getType() );
if ( var.getName() != null )
{
description.append( " " );
description.append( var.getName() );
}
}
description.append( " )" );
RequestLogger.printLine( description.toString() );
}
}
public static final void stopAllRelayInterpreters()
{
for ( Interpreter i : KoLmafiaASH.INTERPRETERS.values() )
{
if ( i.getRelayRequest() != null )
{
i.setState( Interpreter.STATE_EXIT );
}
}
}
}