key0-96/
key0-96/doc/key/
key0-96/doc/key/credits/
key0-96/doc/key/developers/
key0-96/doc/key/developers/resources/
key0-96/setup/caves/
key0-96/setup/help/
key0-96/setup/ruins/
key0-96/src/
key0-96/src/commands/
key0-96/src/events/
key0-96/src/hack/
key0-96/src/sql/
key0-96/src/swing/
key0-96/src/talker/forest/
key0-96/src/talker/objects/
key0-96/src/terminals/
/*
**               j###t  ########## ####   ####
**              j###t   ########## ####   ####
**             j###T               "###L J###"
**          ######P'    ##########  #########
**          ######k,    ##########   T######T
**          ####~###L   ####
**          #### q###L  ##########   .#####
**          ####  \###L ##########   #####"
**
**  $Id$
**
**  Class History
**
**  Date        Name         Description
**  ---------|------------|-----------------------------------------------
**  19Aug98     subtle       start of recorded history
**
*/

package key;

import java.net.*;
import java.io.*;
import java.util.Vector;

/**
  *  This class is a thread which opens and binds a port,
  *  creating new instances of 'InteractiveConnectionion' to
  *  deal with each connection request that comes in.
 */

public class TelnetConnectPort extends Daemon
{
	private static final long serialVersionUID = -9053728784766596365L;
	
	public static final AtomicElement[] ELEMENTS =
	{
		AtomicElement.construct( TelnetConnectPort.class, Integer.TYPE, "port",
			AtomicElement.PUBLIC_FIELD | AtomicElement.READ_ONLY,
			"the port number connections are being accepted on" )
	};
	
	public static final AtomicStructure STRUCTURE = new AtomicStructure( Daemon.STRUCTURE, ELEMENTS );
	
		//  sleep for 5 minutes when we exceed our limit of connections
	public static final int SLEEP_DURATION = 5 * 60 * 1000;
	
	public int port = 2809;
	private transient ServerSocket socket = null;
	
	public TelnetConnectPort()
	{
	}
	
	public TelnetConnectPort( int portNumber )
	{
		port = portNumber;
	}
	
	public TelnetConnectPort( Object key, int portNumber )
	{
		port = portNumber;
		setKey( key );
	}
	
	public AtomicStructure getDeclaredStructure()
	{
		return( STRUCTURE );
	}

	public void argument( String args )
	{
		if( args.length() > 0 )
		{
			try
			{
				port = Integer.parseInt( args );
			}
			catch( NumberFormatException e )
			{
				throw new IllegalArgumentException( "'" + args + "' is not an integer port number" );
			}
		}
	}
	
	public void run()
	{
		while( true )
		{
			try
			{
				serveRequests();
			}
			catch( Throwable t )
			{
				if( t instanceof ThreadDeath )
					throw (ThreadDeath) t;
				else
					Log.fatal( this, t.toString() );
			}
		}
	}
	
	private static final String CONNECTION_LOG = "connection";
	
	private void serveRequests()
	{
		if( socket != null )
		{
			try
			{
				socket.close();
			}
			catch( Exception e )
			{
			}
			
			socket = null;
		}
		
		try
		{
			socket = new ServerSocket( port );
		}
		catch( java.io.IOException e )
		{
			Log.error( e );
			Log.fatal( this, e.toString() );
		}
		
		InetAddress ourAddress = socket.getInetAddress();
		
		String hn = ourAddress.getHostName();
		Log.log( CONNECTION_LOG, "'" + hn + " " + port + "' initialised" );
		
		TelnetIC tc = null;
		ConnectingPlayer cp = null;
		Socket thisConnection = null;
		
		try
		{
			while( true )
			{
				try
				{
					thisConnection = socket.accept();
				}
				catch( java.net.SocketException e )
				{
						//  this almost certainly means we've been killed
					socket.close();
					Log.error( e );
					break;
				}
				
				//  not all these options are supported on all platforms,
				//  but we want to try each of them in case:
				
				try
				{
						//  linger for up to three seconds
					thisConnection.setSoLinger( true, 300 );
				}
				catch( java.net.SocketException e )
				{
				}
				
				try
				{
						//  timeout in an hour of no data
					thisConnection.setSoTimeout( 60 * 60 * 1000 );
				}
				catch( java.net.SocketException e )
				{
				}
					
				try
				{
						//  ensure we're waiting for packets
					thisConnection.setTcpNoDelay( false );
				}
				catch( java.net.SocketException e )
				{
				}
				
				try
				{
					tc = new TelnetIC( thisConnection );
				}
				catch( IOException e )
				{
					continue;
				}
				catch( LimitExceededException e )
				{
						//  generally means we've exceeded our
						//  limit of sockets, or something
						//  really funny has happened
					try
					{
						Thread.sleep( SLEEP_DURATION );
					}
					catch( InterruptedException ex )
					{
					}
					
					continue;
				}
				
				Site site = tc.getSite();
				
				tc.send( "site is " + site.toString() );
				
				if( !site.connectionsAllowed() )
				{
					TextParagraph siteBanned = (TextParagraph) Key.instance().getOnline().getElement( "siteban" );
					if ( siteBanned == null )
						tc.send( "\n\nThis site has been completely banned.\n\n" );
					else tc.send( siteBanned, false );
					
					tc.close();
				}
				else
				{
					cp = new ConnectingPlayer();
					tc.interactWith( cp );
					tc.start();
				}
				
					//  clean up our references so that they
					//  can be garbage collected
				cp = null;
				tc = null;
				thisConnection = null;
				
				yield();
			}
		}
		catch( Exception e )
		{
			if( Key.isRunning() )
			{
				Log.error( "during scan", e );
			}
		}
		catch( Error e )
		{
			Log.error( e );
			throw e;
		}
	}
}