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:
//
// changes@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) {
Pars parser = new Pars();
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 Pars {
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, (String) null, DEFAULTPORT);
return null;
}
private String parseMyInfo(String pChatName, String pParm) {
if (zMyInfo != null)
return "My Info (" + zMyInfo + ") already given.";
Port ap = new Port();
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) {
Port ap = new Port();
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 Port {
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) {
Dots da = new Dots(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++) {
Dots us = new Dots(s);
us.overlayFrom(da);
addAddress(us.toString());
}
return null;
}
}
private static class Dots {
public String[] zParts = null; // Back to Front
public Dots(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(Dots 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);
}
}
}
}