package org.litesoft.p2pchat; import java.io.*; import java.net.*; // Copyright Status: // // All Software available from LiteSoft.org (including this file) is // hereby released into the public domain. // // It is free! As in, you may use it freely in both commercial and // non-commercial applications, bundle it with your software // distribution, include it on a CD-ROM, list the source code in a book, // mirror the documentation at your own web site, or use it in any other // way you see fit. // // NO Warranty! // // All software is provided "as is". // // There is ABSOLUTELY NO WARRANTY OF ANY KIND: not for the design, fitness // (for a particular purpose), level of errors (or lack thereof), or // applicability of this software. The entire risk as to the quality // and performance of this software is with you. Should this software // prove defective, you assume the cost of all necessary servicing, repair // or correction. // // In no event unless required by applicable law or agreed to in writing // will any party who created or may modify and/or redistribute this // software, be liable to you for damages, including any general, // special, incidental or consequential damages arising out of the use or // inability to use this software (including but not limited to loss of // data or data being rendered inaccurate or losses sustained by you or // third parties or a failure of this software to operate with any // other programs), even if such holder or other party has been advised // of the possibility of such damages. // // NOTE: Should you discover a bug, have a recogmendation for a change, wish // to submit modifications, or wish to add new classes/functionality, // please email them to: // // changes44@litesoft.org // /** * @author Devin Smith and George Smith * @version 0.3 02/02/02 Added IllegalArgument.ifNull for all public params that may not be null * @version 0.2 01/28/02 Refactored and Added Licence * @version 0.1 12/27/01 Initial Version */ public abstract class AbstractP2PChat { private static final String VERSION = "0.2"; private static final int DEFAULTPORT = 11581; protected static String getTitle() { return "P2PChat ver " + VERSION; } protected static void dumpHelp( String problem ) { System.out.println( getTitle() ); System.out.println(); System.out.println( problem ); System.out.println(); System.out.println( "This program takes 1-n arguments:" ); System.out.println( " 1st - Chatname" ); System.out.println( " nth - our Address and/or port" ); System.out.println( " peer(s) Address and optional port" ); System.out.println(); System.out.println( " An Address is a standard dotted address (eg 192.168.1.5)" ); System.out.println( " A Port is a simple integer (default: " + DEFAULTPORT + ")" ); System.out.println( " A colon (':') seperates the address and port (eg 192.168.1.5:5432)" ); System.out.println( " Our Address and/or port is indictaed by being surrounded by square" ); System.out.println( " brackets (eg [192.168.1.5:5432])" ); System.exit( 0 ); } abstract protected UserDialog getUserDialog( MyInfo pMyInfo ); protected void init( String[] args ) { ArgsParser parser = new ArgsParser(); String error = parser.parse( args ); if ( error != null ) dumpHelp( error ); try { UserDialog userDialog = getUserDialog( parser.zMyInfo ); PendingPeerManager ppm = new PendingPeerManager( userDialog ); PeerInfo[] initialPeers = parser.zInitialPeers; if ( initialPeers != null ) for ( int i = 0 ; i < initialPeers.length ; i++ ) ppm.addNewPeer( initialPeers[ i ] ); ServerSocket serverSocket = getServerSocket( parser.zMyInfo.getPort() ); new ActivePeerManager( parser.zMyInfo , userDialog , ppm ); while ( true ) { ppm.addNewPeer( serverSocket.accept() ); } } catch ( Exception e ) { e.printStackTrace(); System.exit( 1 ); } } private ServerSocket getServerSocket( int pPort ) { try { return new ServerSocket( pPort ); } catch ( IOException e ) { dumpHelp( "Unable to Open Socket Listener on Port number(" + pPort + "). Error: " + e.getMessage() ); } return null;// Not Reachable } // An Address is a standard dotted address (eg 192.168.1.5) // A Port is a simple integer // A colon (':') seperates the address and port (eg 192.168.1.5:5432) // Our's is indictaed by surrounding square brackets (eg [192.168.1.5:5432]) private static class ArgsParser { public MyInfo zMyInfo = null; public PeerInfo[] zInitialPeers = null; public String parse( String[] args ) { if ( (args == null) || (args.length == 0) ) { return "Not enough parameters."; } String chatName = args[ 0 ].trim(); if ( chatName.length() == 0 ) { return "No chatname specified."; } for ( int i = 1 ; i < args.length ; i++ ) { String err , s = args[ i ]; if ( s.startsWith( "[" ) && s.endsWith( "]" ) ) err = parseMyInfo( chatName , s.substring( 1 , s.length() - 1 ) ); else err = parsePeerInfo( s ); if ( err != null ) return err; } if ( zMyInfo == null ) zMyInfo = new MyInfo( chatName , null , DEFAULTPORT ); return null; } private String parseMyInfo( String pChatName , String pParm ) { if ( zMyInfo != null ) return "My Info (" + zMyInfo + ") already given."; AddressPort ap = new AddressPort(); String err = ap.parse( pParm ); if ( err != null ) return err; zMyInfo = new MyInfo( pChatName , ap.zAddress , ap.zPort ); return null; // No Error } private String parsePeerInfo( String pParm ) { AddressPort ap = new AddressPort(); String err = ap.parse( pParm ); if ( err != null ) return err; if ( ap.zAddress == null ) return "Peer Address not found in: " + pParm; if ( zInitialPeers == null ) zInitialPeers = new PeerInfo[ 1 ]; else { PeerInfo[] temp = new PeerInfo[ zInitialPeers.length + 1 ]; System.arraycopy( zInitialPeers , 0 , temp , 0 , zInitialPeers.length ); zInitialPeers = temp; // From , At , To , At , For } zInitialPeers[ zInitialPeers.length - 1 ] = new PeerInfo( null , ap.zAddress , ap.zPort ); return null; } // An Address is a standard dotted address (eg 192.168.1.5) // A Port is a simple integer // A colon (':') seperates the address and port (eg 192.168.1.5:5432) // Our's is indictaed by surrounding square brackets (eg [192.168.1.5:5432]) private static class AddressPort { public String zAddress = null; public int zPort = DEFAULTPORT; public String parse( String pParm ) { IllegalArgument.ifNull( "Parm" , pParm ); String err; int colonAt = (pParm = pParm.trim()).indexOf( ':' ); if ( colonAt != -1 ) { if ( null != (err = parseAddress( pParm.substring( 0 , colonAt ) )) ) return "Invalid Address in (" + pParm + "): " + err; if ( null != (err = parsePort( pParm.substring( colonAt + 1 ) )) ) return "Invalid Port in (" + pParm + "): " + err; return null; } if ( isAllDigits( pParm ) ) { if ( null != (err = parsePort( pParm )) ) return "Port (" + pParm + ") Invalid: " + err; } else { if ( null != (err = parseAddress( pParm )) ) return "Address (" + pParm + ") Invalid: " + err; } return null; } private String parsePort( String pParm ) { try { zPort = Integer.parseInt( pParm.trim() ); } catch ( NumberFormatException e ) { return "Not a number."; } if ( zPort < 1024 || zPort >= (2 << 16) ) return "Not within range. Must be a 16 bit value greater then 1024"; return null; } private String parseAddress( String pParm ) { if ( (pParm = pParm.trim()).length() == 0 ) { zAddress = MyInfo.getIPs(); return null; } for ( int commaAt ; -1 != (commaAt = pParm.indexOf( ',' )) ; pParm = pParm.substring( commaAt + 1 ).trim() ) { String err = parseAnAddress( pParm.substring( 0 , commaAt ) ); if ( err != null ) return err; } return parseAnAddress( pParm ); } private String parseAnAddress( String pParm ) { int dotAt = pParm.lastIndexOf( '.' ); if ( dotAt == -1 ) return "No dot ('.') in: " + pParm; String lastField = pParm.substring( dotAt + 1 ); if ( isAllDigits( lastField ) ) return parseDirectAddress( pParm ); if ( lastField.length() == 0 ) return "No TLD in: " + pParm; if ( -1 != pParm.indexOf( ' ' ) ) return "Illegal Domain Reference in: " + pParm; String[] directAddresses = convertAddressDomainToDirects( pParm ); if ( directAddresses == null ) return "Unresolvable Domain Reference: " + pParm; for ( int i = 0 ; i < directAddresses.length ; i++ ) addAddress( directAddresses[ i ] ); return null; } private void addAddress( String pAddress ) { if ( zAddress == null ) zAddress = pAddress; else zAddress += "," + pAddress; } private boolean isAllDigits( String pParm ) { if ( pParm.length() == 0 ) return false; for ( int i = pParm.length() ; i-- > 0 ; ) if ( !Character.isDigit( pParm.charAt( i ) ) ) return false; return true; } private String[] convertAddressDomainToDirects( String pParm ) { InetAddress[] addresses; try { addresses = InetAddress.getAllByName( pParm ); } catch ( UnknownHostException e ) { return null; } if ( (addresses == null) || (addresses.length == 0) ) return null; String[] retval = new String[ addresses.length ]; for ( int i = 0 ; i < addresses.length ; i++ ) retval[ i ] = addresses[ i ].toString(); return retval; } private String parseDirectAddress( String pParm ) { DottedAddress da = new DottedAddress( pParm ); for ( int i = 0 ; i < da.zParts.length ; i++ ) if ( !isAllDigits( da.zParts[ i ] ) ) return "Non-Numeric (" + da.zParts[ i ] + ") Dotted Address in: " + pParm; if ( da.zParts.length > 4 ) return "Too Many Parts in Address: " + pParm; if ( da.zParts.length == 4 ) { addAddress( da.toString() ); return null; } String s; for ( int i = 0 ; null != (s = ThisMachine.getIPAddress( i )) ; i++ ) { DottedAddress us = new DottedAddress( s ); us.overlayFrom( da ); addAddress( us.toString() ); } return null; } } private static class DottedAddress { public String[] zParts = null; // Back to Front public DottedAddress( String pParm ) { if ( pParm == null ) return; zParts = new String[ 0 ]; while ( pParm.startsWith( "." ) ) pParm = pParm.substring( 1 ); if ( pParm.length() == 0 ) return; for ( int dotAt ; -1 != (dotAt = pParm.indexOf( '.' )) ; pParm = pParm.substring( dotAt + 1 ) ) addPart( pParm.substring( 0 , dotAt ) ); addPart( pParm ); } private void addPart( String pPart ) { if ( zParts.length == 0 ) zParts = new String[ 1 ]; else { String[] temp = new String[ zParts.length + 1 ]; System.arraycopy( zParts , 0 , temp , 1 , zParts.length ); zParts = temp; // From , At , To , At , For } zParts[ 0 ] = pPart; } public void overlayFrom( DottedAddress pParm ) { if ( pParm != null ) for ( int i = 0 ; i < pParm.zParts.length ; i++ ) this.zParts[ i ] = pParm.zParts[ i ]; } public String toString() { if ( zParts.length == 0 ) return "No Address"; StringBuffer sb = new StringBuffer( 16 ); for ( int i = zParts.length ; i-- > 0 ; ) { sb.append( '.' ); sb.append( zParts[ i ] ); } return sb.toString().substring( 1 ); } } } }