/*
* P2P-Radio - Peer to peer streaming system
* Project homepage: http://p2p-radio.sourceforge.net/
* Copyright (C) 2003-2004 Michael Kaufmann <hallo@michael-kaufmann.ch>
*
* ---------------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* ---------------------------------------------------------------------------
*/
package p2pradio;
import p2pradio.gui.*;
import p2pradio.logging.*;
import p2pradio.sources.*;
import p2pradio.players.*;
import p2pradio.webinterface.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Filter;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Level;
/**
* Starts everything and provides the command-line interface.
* It also manages the message handler for important and
* debug messages.
*
* @author Michael Kaufmann
*/
public class Radio implements Filter
{
public static final String Name = Messages.getString("Radio.NAME"); //$NON-NLS-1$
public static final String Version = "2.0"; //$NON-NLS-1$
public static final String VersionWithoutSpaces = "2.0"; //$NON-NLS-1$
public static final String NameSlashVersion = Name + "/" + VersionWithoutSpaces; //$NON-NLS-1$
public static final String HOMEPAGE = "http://p2p-radio.sourceforge.net/"; //$NON-NLS-1$
public static boolean displayDebugMessages;
public static boolean dontStartMediaPlayerInListeningMode = false;
public static boolean startMediaPlayerInBroadcastingMode = false;
public static boolean startMinimized = false;
public static boolean enableMonitor = false;
public static HttpPlayer httpPlayer;
private Peer peer;
private RadioGUI radioGUI;
public static void main(String args[])
{
try
{
new Radio(args);
}
catch (Exception e)
{
Logger.severe("Radio", "INTERNAL_ERROR", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void printUsageInformation()
{
System.out.println(Messages.getString("Radio.USAGE_INFORMATION", new Object[]{new Integer(Peer.DEFAULT_PORT_NR), new Integer(Peer.MAX_NUMBER_OF_CHILDREN)})); //$NON-NLS-1$
System.out.println();
System.exit(0);
}
private void printIntroduction(int port, boolean consoleUI)
{
System.out.println();
System.out.println(Messages.getString("Radio.WELCOME", Name)); //$NON-NLS-1$
System.out.println();
try
{
System.out.println(Messages.getString("Radio.IP_ADDRESS", InetAddress.getLocalHost().getHostAddress())); //$NON-NLS-1$
}
catch (UnknownHostException e)
{
}
System.out.println(Messages.getString("Radio.PORT_NUMBER", new Integer(port))); //$NON-NLS-1$
System.out.println(Messages.getString("Radio.MP3_STREAM_PORT_NUMBER", new Integer(port + Peer.STREAM_PORT_OFFSET))); //$NON-NLS-1$
System.out.println(Messages.getString("Radio.WEB_SERVER_PORT_NUMBER", new Integer(port + Peer.WEBINTERFACE_PORT_OFFSET))); //$NON-NLS-1$
System.out.println();
if (consoleUI)
{
System.out.println(Messages.getString("Radio.CONSOLE_MODE_HELP", Name)); //$NON-NLS-1$
System.out.println();
}
}
protected Radio(String argsString[])
{
// Handler zuerst sowohl f�r Kommandozeilen- als auch
// f�r GUI-Bedienung aktivieren, damit man alle Fehler
// w�hrend dem Starten sieht
Handler logHandler = new ConsoleLogHandler();
logHandler.setFilter(this);
logHandler.setFormatter(new LogFormatter());
Logger.getLogger().addHandler(logHandler);
// Parameter in einen Vektor kopieren
Vector args = new Vector();
for (int i=0; i < argsString.length; i++)
{
args.add(argsString[i]);
}
// Debug-Nachrichten anzeigen?
displayDebugMessages = parseBooleanArg(args, "debug", false); //$NON-NLS-1$
// Keine grafische Oberfl�che?
boolean noGUI = parseBooleanArg(args, "nogui", false); //$NON-NLS-1$
boolean consoleUI = parseBooleanArg(args, "consoleui", false); //$NON-NLS-1$
// Keine grafische Oberfl�che bei Konsolenbedienung anzeigen
if (consoleUI)
{
noGUI = true;
}
// Portnummern lesen
int port = -1;
boolean portSet = false;
try
{
port = parseIntArg(args, "port", port); //$NON-NLS-1$
portSet = (port != -1);
}
catch (IllegalArgumentException e)
{
exit(Messages.getString("Radio.INVALID_PORT_NUMBER"), noGUI); //$NON-NLS-1$
}
// Maximale Bandbreite lesen
int maxUploadBandwidth = 0;
boolean maxUploadBandwidthSet = false;
try
{
maxUploadBandwidth = parseIntArg(args, "maxbandwidth", maxUploadBandwidth); //$NON-NLS-1$
maxUploadBandwidthSet = (maxUploadBandwidth != 0);
}
catch (IllegalArgumentException e)
{
exit(Messages.getString("Radio.INVALID_BANDWIDTH_LIMIT"), noGUI); //$NON-NLS-1$
}
// Logdatei gew�nscht?
String logFileName = null;
logFileName = parseStringArg(args, "logfile", null); //$NON-NLS-1$
if (logFileName != null)
{
try
{
Handler logfileHandler = new FileLogHandler(logFileName);
logfileHandler.setFilter(this);
logfileHandler.setFormatter(new LogFormatter());
Logger.getLogger().addHandler(logfileHandler);
}
catch (IOException e)
{
exit(Messages.getString("Radio.INVALID_LOGFILE_NAME"), noGUI); //$NON-NLS-1$
}
}
// Den Strom signieren?
Peer.signStreamPackets = parseBooleanArg(args, "sign", Peer.signStreamPackets); //$NON-NLS-1$
Peer.signStreamPackets = !parseBooleanArg(args, "dontsign", !Peer.signStreamPackets); //$NON-NLS-1$
// Authentizit�t des Stroms �berpr�fen?
Peer.verifyStreamPackets = parseBooleanArg(args, "verify", Peer.verifyStreamPackets); //$NON-NLS-1$
Peer.verifyStreamPackets = !parseBooleanArg(args, "dontverify", !Peer.verifyStreamPackets); //$NON-NLS-1$
// Media Player starten?
startMediaPlayerInBroadcastingMode = parseBooleanArg(args, "startmediaplayer", startMediaPlayerInBroadcastingMode); //$NON-NLS-1$
dontStartMediaPlayerInListeningMode = parseBooleanArg(args, "dontstartmediaplayer", dontStartMediaPlayerInListeningMode); //$NON-NLS-1$
// Minimiert starten?
startMinimized = parseBooleanArg(args, "startminimized", startMinimized); //$NON-NLS-1$
// Monitor unterst�tzen?
enableMonitor = parseBooleanArg(args, "enablemonitor", enableMonitor); //$NON-NLS-1$
enableMonitor = !parseBooleanArg(args, "disablemonitor", !enableMonitor); //$NON-NLS-1$
if ((args.size() == 0) && !noGUI)
{
// Grafische Oberfl�che anzeigen und
// Fragen stellen
radioGUI = new RadioGUI(portSet, port, maxUploadBandwidthSet, maxUploadBandwidth, logHandler);
return;
}
if (args.size() < 2)
{
// Zuwenige Parameter angegeben
printUsageInformation();
}
// Entweder grafische Oberfl�che ohne Fragen oder
// nur Kommandozeile
if (((String)args.get(0)).toLowerCase().equals("server")) //$NON-NLS-1$
{
URL playlist = null;
try
{
playlist = new URL((String)args.get(1));
}
catch (MalformedURLException e)
{
exit(Messages.getString("INVALID_PLAYLIST_ADDRESS"), noGUI); //$NON-NLS-1$
}
InetSocketAddress monitorAddress = null;
// Monitoradresse lesen
if (args.size() >= 3)
{
if (!enableMonitor)
{
System.out.println(Messages.getString("Radio.MONITOR_DEACTIVATED_ADDRESS_IGNORED")); //$NON-NLS-1$
}
else
{
String addr = (String)args.get(2);
String remoteHost;
int remotePort;
int pos;
if ((pos = addr.indexOf(":")) == -1) //$NON-NLS-1$
{
remoteHost = addr;
remotePort = Peer.MONITOR_PORT;
}
else
{
try
{
remoteHost = addr.substring(0, pos);
remotePort = Integer.parseInt(addr.substring(pos+1, addr.length()));
}
catch (NumberFormatException e)
{
remoteHost = addr;
remotePort = Peer.MONITOR_PORT;
}
}
try
{
monitorAddress = new InetSocketAddress(remoteHost, remotePort);
if (monitorAddress.getAddress() == null)
{
throw new IllegalArgumentException();
}
}
catch (Exception e)
{
System.out.println(Messages.getString("INVALID_MONITOR_ADDRESS")); //$NON-NLS-1$
}
if (monitorAddress.getAddress().isLoopbackAddress())
{
System.out.println(Messages.getString("Radio.LOCAL_MONITOR_ADDRESS", addr)); //$NON-NLS-1$
}
}
}
if (!portSet)
{
port = Radio.getBestPortNumber();
}
if (noGUI)
{
printIntroduction(port, consoleUI);
}
try
{
peer = new Peer(port, maxUploadBandwidth, monitorAddress);
}
catch (PeerException e)
{
exit(e.getMessage(), noGUI);
}
Logger.finer("Radio", "I_AM_THE_SERVER", peer); //$NON-NLS-1$ //$NON-NLS-2$
HttpSource source = new HttpSource(peer.getBroadcastBuffer(), playlist);
Logger.info("Radio", "CONNECTING_TO_SERVER"); //$NON-NLS-1$ //$NON-NLS-2$
if (!source.connect())
{
Logger.info("Radio", "COULD_NOT_CONNECT_TO_SERVER"); //$NON-NLS-1$ //$NON-NLS-2$
exitWithLogger(Messages.getString("COULD_NOT_CONNECT_TO_SERVER"), noGUI); //$NON-NLS-1$
}
source.start();
}
else if (((String)args.get(0)).toLowerCase().equals("client")) //$NON-NLS-1$
{
// Serveradresse lesen
String addr = removePrefix((String)args.get(1));
String remoteHost;
int remotePort;
int pos;
if ((pos = addr.indexOf(":")) == -1) //$NON-NLS-1$
{
remoteHost = addr;
remotePort = Peer.DEFAULT_PORT_NR;
}
else
{
try
{
remoteHost = addr.substring(0, pos);
remotePort = Integer.parseInt(addr.substring(pos+1, addr.length()));
}
catch (NumberFormatException e)
{
remoteHost = addr;
remotePort = Peer.DEFAULT_PORT_NR;
}
}
// Die Adresse des Servers
InetSocketAddress serverAddress = null;
try
{
serverAddress = new InetSocketAddress(remoteHost, remotePort);
if (serverAddress.getAddress() == null)
{
throw new IllegalArgumentException();
}
}
catch (Exception e)
{
exit(Messages.getString("INVALID_SERVER_ADDRESS"), noGUI); //$NON-NLS-1$
}
if (!portSet)
{
port = Radio.getBestPortNumber();
}
if (noGUI)
{
printIntroduction(port, consoleUI);
}
try
{
peer = new Peer(new RemotePeer(serverAddress), port, maxUploadBandwidth);
}
catch (PeerException e)
{
exit(e.getMessage(), noGUI);
}
Logger.finer("Radio", "I_AM_A_CLIENT", peer); //$NON-NLS-1$ //$NON-NLS-2$
}
else
{
printUsageInformation();
}
httpPlayer = new HttpPlayer(peer.getBuffer(), peer.getSocketAddress().getPort() + Peer.STREAM_PORT_OFFSET);
httpPlayer.start();
new WebServer(peer, httpPlayer).start();
if (!noGUI)
{
// Grafische Oberfl�che ab jetzt anzeigen
radioGUI = new RadioGUI(logHandler);
// Die Bandbreite ist so wichtig, dass sie unbedingt noch
// abgefragt werden muss
if (peer.isServer() && !maxUploadBandwidthSet)
{
peer.setMaxUploadBandwidth(radioGUI.inputMaxUploadBandwidth());
}
}
// Media Player nur automatisch starten, falls der Peer
// kein Server ist
if ((peer.isServer() && Radio.startMediaPlayerInBroadcastingMode) || (!peer.isServer() && !Radio.dontStartMediaPlayerInListeningMode))
{
new MediaPlayerLauncher(peer.getListenBuffer(), httpPlayer).start();
}
if (!noGUI)
{
radioGUI.setPeer(peer);
}
else
{
if (consoleUI)
{
// Befehle entgegennehmen
BufferedReader commandLine = new BufferedReader(new InputStreamReader(System.in));
while (true)
{
String command = null;
try
{
command = commandLine.readLine();
}
catch (Exception e)
{
Logger.severe("Radio", "INTERNAL_ERROR", e); //$NON-NLS-1$ //$NON-NLS-2$
exitWithLogger(Messages.getString("INTERNAL_ERROR"), noGUI); //$NON-NLS-1$
}
if (command == null)
{
// Ctrl-C
peer.leave();
System.exit(0);
}
if (command.equals("help") || command.equals("?")) //$NON-NLS-1$ //$NON-NLS-2$
{
printIntroduction(peer.getSocketAddress().getPort(), consoleUI);
}
else if (command.equals("exit")) //$NON-NLS-1$
{
peer.leave();
System.exit(0);
}
else if (command.equals("join")) //$NON-NLS-1$
{
peer.join();
}
else if (command.equals("change")) //$NON-NLS-1$
{
peer.changeSupplier(true, false);
}
else if (command.equals("leave")) //$NON-NLS-1$
{
peer.leave();
}
else if (command.equals("debug")) //$NON-NLS-1$
{
displayDebugMessages = !displayDebugMessages;
}
else
{
System.out.println(Messages.getString("Radio.UNKNOWN_COMMAND")); //$NON-NLS-1$
}
}
}
else
{
while(true)
{
try
{
Thread.sleep(100000);
}
catch (InterruptedException e)
{
}
}
}
}
}
public static int getBestPortNumber()
{
if (isPortNrOK(Peer.DEFAULT_PORT_NR))
{
return Peer.DEFAULT_PORT_NR;
}
else
{
return getRandomPortNumber();
}
}
/**
* Returns a random non-privileged (bigger than 1023) port number. This method checks that
* the returned port number and the two following port numbers are free for both TCP and UDP.
*/
private static int getRandomPortNumber()
{
// Drei Portnummern nacheinander m�ssen frei sein
// (es wird sowohl TCP als auch UDP getestet)
Random random = new Random();
int portNr = 1024 + random.nextInt((65535 - 2) - 1024);
if (!isPortNrOK(portNr)) return getRandomPortNumber();
if (!isPortNrOK(portNr+1)) return getRandomPortNumber();
if (!isPortNrOK(portNr+2)) return getRandomPortNumber();
// portNr ist OK
return portNr;
}
private static boolean isPortNrOK(int portNr)
{
try
{
ServerSocket serverSocket = new ServerSocket(portNr);
serverSocket.close();
}
catch(IOException e)
{
return false;
}
try
{
DatagramSocket datagramSocket = new DatagramSocket(portNr);
datagramSocket.close();
}
catch(IOException e)
{
return false;
}
return true;
}
protected static final boolean parseBooleanArg(Vector args, String parameter, boolean defaultValue)
{
for (int i=0; i < args.size(); i++)
{
String arg = ((String)args.get(i)).toLowerCase();
if (arg.equals("-" + parameter.toLowerCase()) || arg.equals("--" + parameter.toLowerCase()) || arg.equals("/" + parameter.toLowerCase())) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
{
args.remove(i);
return true;
}
}
return defaultValue;
}
protected static final int parseIntArg(Vector args, String parameter, int defaultValue)
{
for (int i=0; i < args.size(); i++)
{
String arg = ((String)args.get(i)).toLowerCase();
if (arg.equals("-" + parameter.toLowerCase()) || arg.equals("--" + parameter.toLowerCase()) || arg.equals("/" + parameter.toLowerCase())) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
{
try
{
int result = Integer.parseInt((String)args.get(i+1));
args.remove(i+1);
args.remove(i);
return result;
}
catch (NumberFormatException e)
{
throw new IllegalArgumentException();
}
}
}
return defaultValue;
}
protected static final String parseStringArg(Vector args, String parameter, String defaultValue)
{
for (int i = 0; i < args.size(); i++)
{
String arg = ((String)args.get(i)).toLowerCase();
if (arg.equals("-" + parameter.toLowerCase()) || arg.equals("--" + parameter.toLowerCase()) || arg.equals("/" + parameter.toLowerCase())) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
{
String result = (String)args.get(i+1);
args.remove(i+1);
args.remove(i);
return result;
}
}
return defaultValue;
}
public boolean isLoggable(LogRecord record)
{
if (record.getLevel().intValue() < Level.INFO.intValue())
{
return displayDebugMessages;
}
else
{
return true;
}
}
public static String removePrefix(String url)
{
// Remove the "p2p-radio://" prefix
if (url.toLowerCase().startsWith("p2p-radio:")) //$NON-NLS-1$
{
url = url.substring(10, url.length());
}
if (url.startsWith("//")) //$NON-NLS-1$
{
url = url.substring(2, url.length());
}
else if (url.startsWith("/")) //$NON-NLS-1$
{
url = url.substring(1, url.length());
}
if (url.endsWith("/")) //$NON-NLS-1$
{
url = url.substring(0, url.length() - 1);
}
return url;
}
private void exit(String message, boolean noGUI)
{
if (message != null)
{
System.out.println(message);
if (!noGUI)
{
RadioGUI.setLookAndFeel();
javax.swing.JOptionPane.showMessageDialog(null, message, Messages.getString("GENERAL_ERROR"), javax.swing.JOptionPane.ERROR_MESSAGE, null); //$NON-NLS-1$
}
}
System.exit(1);
}
private void exitWithLogger(String message, boolean noGUI)
{
if (message != null)
{
if (!noGUI)
{
RadioGUI.setLookAndFeel();
javax.swing.JOptionPane.showMessageDialog(null, message, Messages.getString("GENERAL_ERROR"), javax.swing.JOptionPane.ERROR_MESSAGE, null); //$NON-NLS-1$
}
}
System.exit(1);
}
}