package org.openslx.satserver.util;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import org.apache.log4j.Logger;
import org.schwering.irc.lib.IRCConfig;
import org.schwering.irc.lib.IRCConfigBuilder;
import org.schwering.irc.lib.IRCConnection;
import org.schwering.irc.lib.IRCConnectionFactory;
import org.schwering.irc.lib.IRCEventListener;
import org.schwering.irc.lib.IRCExceptionHandler;
import org.schwering.irc.lib.IRCSSLSupport;
import org.schwering.irc.lib.IRCTrafficLogger;
import org.schwering.irc.lib.IRCUser;
import org.schwering.irc.lib.util.IRCModeParser;
public class IrcClient
{
private static final Logger LOGGER = Logger.getLogger( IrcClient.class );
private static final Map<String, Wrapper> connections = new HashMap<>();
static
{
new Thread() {
public void run()
{
while ( !Thread.interrupted() ) {
try {
Thread.sleep( 60000 );
} catch ( InterruptedException e ) {
return;
}
List<Wrapper> copy;
synchronized ( connections ) {
copy = new ArrayList<Wrapper>( connections.values() );
}
for ( Wrapper c : copy ) {
c.ping();
}
}
};
}.start();
}
public static void sendMessage( String serverAddress, String channel, String nick, String message, List<String> outErrors )
{
String host = serverAddress;
Wrapper wrapper = null;
int port = -1;
boolean ssl = false;
synchronized ( connections ) {
wrapper = connections.get( serverAddress );
if ( wrapper == null ) {
port = 6667;
int i = serverAddress.indexOf( ':', serverAddress.lastIndexOf( ']' ) + 1 );
if ( i != -1 ) {
host = serverAddress.substring( 0, i );
if ( i + 1 < serverAddress.length() && serverAddress.charAt( i + 1 ) == '+' ) {
ssl = true;
i++;
}
port = Util.parseInt( serverAddress.substring( i + 1 ), 0 );
}
if ( Util.isEmpty( host ) ) {
outErrors.add( "Host is empty after parsing" );
return;
}
if ( host.charAt( 0 ) == '[' && host.length() > 2 ) {
host = host.substring( 1, host.length() - 1 );
}
IRCSSLSupport sslSupport = null;
if ( ssl ) {
sslSupport = new IRCSSLSupport()
{ // Return null everywhere so java uses the defaults
@Override
public TrustManager[] getTrustManagers()
{
return null;
}
@Override
public SecureRandom getSecureRandom()
{
return null;
}
@Override
public KeyManager[] getKeyManagers()
{
return null;
}
};
}
wrapper = new Wrapper( serverAddress );
IRCConfig config = IRCConfigBuilder.newBuilder().autoPong( true ).encoding( "UTF-8" ).host( host )
.port( port ).nick( nick ).timeout( 70000 ).exceptionHandler( wrapper ).trafficLogger( IRCTrafficLogger.SYSTEM_OUT )
.sslSupport( sslSupport ).build();
IRCConnection connection = IRCConnectionFactory.newConnection( config );
wrapper.setConnection( connection );
connections.put( serverAddress, wrapper );
}
} // sync end
synchronized ( wrapper ) {
if ( !wrapper.connection.isConnected() ) {
try {
wrapper.connection.connect();
} catch ( KeyManagementException | NoSuchAlgorithmException | IOException e ) {
LOGGER.warn( "Cannot connect to " + host + " port " + port + " SSL: " + ssl, e );
outErrors.add( "Cannot connect to " + host + " port " + port + " SSL: " + ssl );
return;
}
}
if ( wrapper.connection.isConnected() ) {
wrapper.sendMessage( channel, message );
}
}
}
static class Wrapper implements IRCExceptionHandler, IRCEventListener
{
private IRCConnection connection;
private final String host;
private final Set<String> channels = new HashSet<>();
public Wrapper( String host )
{
this.host = host;
}
public synchronized void ping()
{
LOGGER.debug( "Pinging " + host );
try {
connection.send( "PING keepalive" );
} catch ( Exception e ) {
}
}
public synchronized void sendMessage( String channel, String message )
{
try {
if ( !channels.contains( channel.toLowerCase() ) ) {
connection.doJoin( channel );
}
connection.doPrivmsg( channel, message );
} catch ( Exception e ) {
}
}
public void setConnection( IRCConnection connection )
{
if ( this.connection != null )
throw new RuntimeException( "Connection already set" );
this.connection = connection;
connection.addIRCEventListener( this );
}
/*
* IRCExceptionHandler
*/
@Override
public void exception( IRCConnection connection, Throwable exception )
{
LOGGER.warn( "IRC Exception for " + host, exception );
if ( exception instanceof IOException ) {
connection.close();
}
}
/*
* IRCEventListener
*/
@Override
public void onRegistered()
{
LOGGER.debug( "Registered" );
}
@Override
public void onDisconnected()
{
LOGGER.warn( host + " is dead." );
synchronized ( connections ) {
connections.remove( this.host );
}
}
@Override
public void onError( String msg )
{
// TODO Auto-generated method stub
}
@Override
public void onError( int num, String msg )
{
// TODO Auto-generated method stub
}
@Override
public void onInvite( String chan, IRCUser user, String passiveNick )
{
// TODO Auto-generated method stub
}
@Override
public synchronized void onJoin( String chan, IRCUser user )
{
if ( user.getNick().equalsIgnoreCase( connection.getNick() ) ) {
LOGGER.debug( "I joined " + chan );
channels.add( chan.toLowerCase() );
}
}
@Override
public synchronized void onKick( String chan, IRCUser user, String passiveNick, String msg )
{
if ( passiveNick.equalsIgnoreCase( connection.getNick() ) ) {
LOGGER.debug( "I was kicked from " + chan );
channels.remove( chan.toLowerCase() );
}
}
@Override
public void onMode( String chan, IRCUser user, IRCModeParser modeParser )
{
// TODO Auto-generated method stub
}
@Override
public void onMode( IRCUser user, String passiveNick, String mode )
{
// TODO Auto-generated method stub
}
@Override
public void onNick( IRCUser user, String newNick )
{
// TODO Auto-generated method stub
}
@Override
public void onNotice( String target, IRCUser user, String msg )
{
// TODO Auto-generated method stub
}
@Override
public synchronized void onPart( String chan, IRCUser user, String msg )
{
if ( user.getNick().equalsIgnoreCase( connection.getNick() ) ) {
LOGGER.debug( "I left " + chan );
channels.remove( chan.toLowerCase() );
}
}
@Override
public void onPing( String ping )
{
// TODO Auto-generated method stub
}
@Override
public void onPrivmsg( String target, IRCUser user, String msg )
{
// TODO Auto-generated method stub
}
@Override
public void onQuit( IRCUser user, String msg )
{
// TODO Auto-generated method stub
}
@Override
public void onReply( int num, String value, String msg )
{
// TODO Auto-generated method stub
}
@Override
public void onTopic( String chan, IRCUser user, String topic )
{
// TODO Auto-generated method stub
}
@Override
public void unknown( String prefix, String command, String middle, String trailing )
{
// TODO Auto-generated method stub
}
}
}