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 key.primitive.*;
import java.util.Enumeration;
import java.io.*;
import java.util.StringTokenizer;

/**
  *  It's too hard to give sites a unique name, but its useful to
  *  store them in a container to save us writing custom load/save
  *  routines.  It also means if you *do* know a sites 'number' on
  *  the program, you have a chance ;)
 */
public class Site extends Atom implements Subnet
{
	private static final long serialVersionUID = 7084061282031417477L;
	public static final AtomicElement[] ELEMENTS =
	{
		AtomicElement.construct( Site.class, Paragraph.class, "notes",
			AtomicElement.PUBLIC_FIELD,
			"some comments about this site" ),
		AtomicElement.construct( Site.class, String.class, "banType",
			AtomicElement.PUBLIC_FIELD,
			"the type of the ban on this site: N, C, or O" ),
		AtomicElement.construct( Site.class, Player.class, "bannedBy",
			AtomicElement.PUBLIC_FIELD,
			"the player who banned this site" ),
		AtomicElement.construct( Site.class, DateTime.class, "bannedAt",
			AtomicElement.PUBLIC_FIELD,
			"when this site was banned" ),
		AtomicElement.construct( Site.class, DateTime.class, "bannedUntil",
			AtomicElement.PUBLIC_FIELD,
			"when this site will be un-banned" ),
		AtomicElement.construct( Site.class, TimeStatistics.class, "connectionStats",
			AtomicElement.PUBLIC_FIELD,
			"information about who connects from this site" ),
		AtomicElement.construct( Site.class, String.class, "alpha",
			AtomicElement.PUBLIC_FIELD | AtomicElement.READ_ONLY,
			"the domain name of this site" ),
		AtomicElement.construct( Site.class, String.class, "reasonBanned",
			AtomicElement.PUBLIC_FIELD,
			"the reason this site was banned" )
	};
	
	public static final AtomicStructure STRUCTURE = new AtomicStructure( Atom.STRUCTURE, ELEMENTS );
	
	public Paragraph notes = new TextParagraph( TextParagraph.CENTERALIGNED, "" );
	
	/**
	  *  The type of ban that is on the site:
	  *  N for newbie
	  *  C for siteban
	  *  0 length string for no ban at all
	 */
	public String banType = "O";
	public Reference bannedBy = Reference.EMPTY;
	public DateTime bannedAt = null;
	public DateTime bannedUntil = null;
	public final TimeStatistics connectionStats = (TimeStatistics) Factory.makeAtom( TimeStatistics.class, "connectionStats" );
	public String reasonBanned = "";
	
	/**
	  *  The IP mask.  This is basically
	  *  the array that was returned by InetAddress
	 */
	public int[] mask;

	/**
	  *  The alpha for this site.  For instance,
	  *  UTS is uts.edu.au (thats the whole site
	  *  name).  (Can be null if no DNS entry)
	 */
	public String alpha;

	private final String SITE_LOG = "site";
	
	public Site()
	{
	}
	
	public AtomicStructure getDeclaredStructure()
	{
		return( STRUCTURE );
	}
	
	public final boolean newbiesAllowed()
	{
		checkBans();
		return( !banType.equals( "N" ) );
	}
	
	public final boolean connectionsAllowed()
	{
		checkBans();
		return( !banType.equals( "C" ) );
	}

	public final TimeStatistics connectionStats()
	{
		return( connectionStats );
	}
	
	/*
	public final TimeStatistics connectionStatistics()
	{
		return( connectionStats );
	}
	*/

	/**
	  *  The passed in string MUST be lowercase
	 */
	protected void resetTrailer( String newT )
	{
		if( alpha != null && !alpha.equals( newT ) )
			((Network)getParent()).unRegisterTrailer( alpha );
		
		alpha = newT;
		
		((Network)getParent()).registerTrailer( this );
	}
	
	/**
	  *  You can construct a Site with any IP, but
	  *  only the relevant IP's will be stored.
	 */
	public Site( int[] ip )
	{
		setMask( ip );
	}
	
	public void setMask( int[] ip )
	{
		if( ip[0] < 128 )
			mask = new int[1];
		else if( ip[0] < 192 )
			mask = new int[2];
		else if( ip[0] < 224 )
			mask = new int[3];
		else
			mask = new int[4];
		
		for( int i = 0; i < mask.length; i++ )
			mask[i] = ip[i];
		
		alpha = null;
		
		setKey( new Integer( mask[ mask.length - 1 ] ) );
	}
	
	public int[] getMask()
	{
		return( mask );
	}

	public String getTrailer()
	{
		return( alpha );
	}

	/**
	  *  Some sites will have dns entries for certain IP's
	  *  within them, and not have entries for others.  We
	  *  also need this code to find the 'lowest common
	  *  denominator' in the site alpha.
	 */
	public void registerSite( String dns )
	{
		if( dns != null )
		{
			if( alpha == null )
				resetTrailer( dns.toLowerCase() );
			else
			{
				dns = dns.toLowerCase();
				String newTrailer = alpha;
				while( !dns.endsWith( newTrailer ) )
				{
						//  okay, we need to find the common parts of these
						//  two strings, from the end.  For instance, the
						//  alpha might currently be 'progsoc.uts.edu.au',
						//  as we've only had connections from 138.25. from
						//  within progsoc.  However, when we get a connection
						//  from say, socs: 'charlie.socs.uts.edu.au', then
						//  the tailer is known to be inaccurate.

					int tIndex = newTrailer.indexOf( '.' );
					if( tIndex == -1 )
					{
						Log.log( SITE_LOG, "ran out of trail sections trying to find a common DNS section" );
						return;
					}
					
					try
					{
						newTrailer = newTrailer.substring( tIndex+1 );
					}
					catch( StringIndexOutOfBoundsException e )
					{
						Log.log( SITE_LOG, "got a trailing period in a alpha '" + alpha + "'.  aborting registerSite()" );
						return;
					}
				}
				
				resetTrailer( newTrailer );
			}
		}
		
		//Log.log( SITE_LOG, "DNS entry '" + dns + "' registered inside " + toString() );
	}
	
	public String toString()
	{
		StringBuffer sb = new StringBuffer();
		
		for( int i = 0; i < mask.length; i++ )
		{
			sb.append( mask[i] );
			sb.append( "." );
		}
		
		if( alpha != null )
			return( alpha + " [" + sb.toString() + "]" );
		else
			return( sb.toString() );
	}
	
	private void checkBans()
	{
		DateTime now = new DateTime();

		if( bannedUntil == null )
			return;

		long diff = now.getTime() - bannedUntil.getTime();

		if( diff >= 0 ) 
		{
				//Ban has expired
			banType = "";
			bannedBy = null;
			bannedAt = now;
			bannedUntil = null;
			reasonBanned = "Site ban expired";
		}
	}
	
	public final void setBan( String banType, Player p, DateTime now, DateTime until, String reason )
	{
		permissionList.check( modifyAction );
		
		banType = banType;
		bannedBy = p.getThis();
		bannedAt = now;
		bannedUntil = until;
		reasonBanned = reason;
	}
}