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 ##########   #####"
*/

package key.util;

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

public class Ident extends Thread
{
	public static final int DEFAULT_PORT = 113;
	
	Socket fromSocket;
	Socket identSocket;
	
	Callback back;
	Writer output;
	PushbackInputStream inputStream;
	
	public Ident( Socket s, Callback cb )
	{
		fromSocket = s;
		back = cb;
	}
	
	public void run()
	{
		Socket is = null;
		
		try
		{
			identSocket = new Socket( fromSocket.getInetAddress(), DEFAULT_PORT );
			
				//  give up after 30 seconds
			identSocket.setSoTimeout( 30 * 1000 );
			
			output = new BufferedWriter( new OutputStreamWriter( identSocket.getOutputStream() ) );
			inputStream = new PushbackInputStream( identSocket.getInputStream() );
			
				//  query the socket
			send( fromSocket.getPort() + "," + fromSocket.getLocalPort() );
			
			String result = input();
			
			StringTokenizer st = new StringTokenizer( result, ":" );
			
				//  skip ports
			if( !st.hasMoreTokens() )
				return;
			st.nextToken();
			
			if( !st.hasMoreTokens() )
				return;
			
			String responseType = st.nextToken().trim();
			
			if( responseType.equalsIgnoreCase( "USERID" ) )
			{
				String opsys = st.nextToken().trim();
				
				if( st.hasMoreTokens() )
					back.identResolvedUserId( opsys.toLowerCase(), st.nextToken("").trim() );
				else
					back.identProtocolError( result );
			}
			else if( responseType.equalsIgnoreCase( "ERROR" ) )
			{
				if( st.hasMoreTokens() )
					back.identResolvedError( st.nextToken().toLowerCase().trim() );
				else
					back.identProtocolError( result );
			}
			else
			{
				back.identProtocolError( result );
			}
		}
		catch( IOException e )
		{
			back.identProtocolError( e.getMessage() );
		}
		finally
		{
			try
			{
				if( output != null )
					output.close();
			}
			catch( Exception e )
			{
			}
			
			try
			{
				if( inputStream != null )
					inputStream.close();
			}
			catch( Exception e )
			{
			}
			
			try
			{
				if( is != null )
					is.close();
			}
			catch( Exception e )
			{
			}
		}
	}
	
	public static final String CRLF = "\r\n";
	public static final int INPUT_BUFFER = 640;
	public static final int MAX_OVERRUN = 640;
	
	private char[] inBuffer = new char[INPUT_BUFFER+2];
	private int lastCharWas = -1;
	
	public String input() throws IOException
	{
		int where=0;
		char b=' ';
		
		do
		{
			int i;
			
				//  continuously skip until a CR or LF
				//  when the buffer runs out
			if( where >= INPUT_BUFFER )
			{
				int count = 0;
				do
				{
					i = inputStream.read();
					count++;
				} while( !( i == '\r' || b == '\n' ) && (count < MAX_OVERRUN) );
				
				if( count == MAX_OVERRUN )
					throw new IOException( "max overrun reached" );
			}
			else
				i = inputStream.read();
			
			if( i != -1 )
				b = (char) i;
			else
				throw new IOException( "connection closed" );
			
			if( b >= 32 && b <= 126 )    //  valid ascii characters only
				inBuffer[where++] = (char) b;
			else if( b == 8 || b == 127 )
			{
				if( where > 0 )
					where--;
			}
		} while( !((b == '\r') && !(lastCharWas == '\n')) && !((b == '\n') && !(lastCharWas == '\r' )) );
		
		lastCharWas = b;
		
		return( new String( inBuffer, 0, where ) );
	}
	
	public void send( String message ) throws IOException
	{
		output.write( message );
		output.write( CRLF );
		output.flush();
	}
	
	public interface Callback
	{
		public void identResolvedError( String error );
		public void identProtocolError( String error );
		public void identResolvedUserId( String machine, String user );
	}
}