/**
* 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.webui;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;
import net.sourceforge.kolmafia.KoLConstants;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.StaticEntity;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.utilities.FileUtilities;
import net.sourceforge.kolmafia.utilities.StringUtilities;
public class RelayServer
implements Runnable
{
public static final Set<RelayAgent> agentThreads = new HashSet<RelayAgent>();
private static long lastStatusMessage = 0;
private static Thread relayThread = null;
private ServerSocket serverSocket = null;
private static int port = 60080;
private static boolean listening = false;
private static boolean updateStatus = false;
private static final RelayServer INSTANCE = new RelayServer();
private static final StringBuffer statusMessages = new StringBuffer();
private RelayServer()
{
for ( int i = 0; i < KoLConstants.RELAY_FILES.length; ++i )
{
FileUtilities.loadLibrary( KoLConstants.RELAY_LOCATION, KoLConstants.RELAY_DIRECTORY, KoLConstants.RELAY_FILES[ i ] );
}
Preferences.setString( "lastRelayUpdate", StaticEntity.getVersion() );
}
public static final void updateStatus()
{
RelayServer.updateStatus = true;
}
public static synchronized final void startThread()
{
if ( RelayServer.relayThread == null )
{
Thread relayServer = new Thread( RelayServer.INSTANCE, "LocalRelayServer" );
relayServer.start();
RelayServer.relayThread = relayServer;
}
}
public static final int getPort()
{
return RelayServer.port;
}
public static final String trimPrefix( final String location )
{
// Remove prefix that will connect to our own relay socket
if ( !location.startsWith( "http://" ) )
{
return location;
}
int pathIndex = location.indexOf( "/", 7 );
if ( pathIndex == -1 )
{
return location;
}
int colon = location.indexOf( ":", 7 );
if ( colon == -1 || colon > pathIndex )
{
return location;
}
int port = StringUtilities.parseInt( location.substring( colon + 1, pathIndex ) );
if ( port != RelayServer.port )
{
return location;
}
try
{
String host = location.substring( 7, colon );
InetAddress address = InetAddress.getByName( host );
if ( address.isLoopbackAddress() )
{
return location.substring( pathIndex + 1 );
}
}
catch ( UnknownHostException e )
{
}
return location;
}
public static final boolean isRunning()
{
return RelayServer.listening;
}
public static final void stop()
{
RelayServer.listening = false;
}
public void run()
{
RelayServer.port = 60080;
while ( !this.openServerSocket() )
{
if ( RelayServer.port <= 60089 )
{
++RelayServer.port;
}
else
{
KoLmafia.quit();
}
}
RelayServer.listening = true;
while ( RelayServer.listening )
{
try
{
this.dispatchAgent( this.serverSocket.accept() );
}
catch ( Exception e )
{
// If an exception occurs here, that means
// someone closed the thread; just reset
// the listening state and fall through.
RelayServer.listening = false;
}
}
this.closeAgents();
try
{
if ( this.serverSocket != null )
{
this.serverSocket.close();
}
}
catch ( Exception e )
{
// The end result of a socket closing
// should not throw an exception, but
// if it does, the socket closes.
}
this.serverSocket = null;
RelayServer.relayThread = null;
}
private synchronized boolean openServerSocket()
{
try
{
if ( Preferences.getBoolean( "relayAllowRemoteAccess" ) )
{
this.serverSocket = new ServerSocket( RelayServer.port, 25 );
}
else
{
this.serverSocket = new ServerSocket( RelayServer.port, 25, InetAddress.getByName( "127.0.0.1" ) );
}
return true;
}
catch ( Exception e )
{
return false;
}
}
private synchronized void closeAgents()
{
for ( RelayAgent agent : agentThreads )
{
agent.setSocket( null );
}
agentThreads.clear();
}
private synchronized void dispatchAgent( final Socket socket )
{
for ( RelayAgent agent : agentThreads )
{
if ( agent.isWaiting() )
{
agent.setSocket( socket );
return;
}
}
this.createAgent( socket );
}
private synchronized void createAgent( final Socket socket )
{
RelayAgent agent = new RelayAgent( RelayServer.agentThreads.size() );
agent.setSocket( socket );
RelayServer.agentThreads.add( agent );
agent.start();
}
public static final void addStatusMessage( final String message )
{
if ( System.currentTimeMillis() - RelayServer.lastStatusMessage < 4000 )
{
RelayServer.statusMessages.append( message );
}
}
public static final String getNewStatusMessages()
{
if ( RelayServer.updateStatus )
{
RelayServer.updateStatus = false;
RelayServer.statusMessages.append( "<!-- REFRESH -->" );
}
String newMessages = RelayServer.statusMessages.toString();
RelayServer.statusMessages.setLength( 0 );
RelayServer.lastStatusMessage = System.currentTimeMillis();
return newMessages;
}
}