summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/satserver/util/IrcClient.java
blob: f7b6632710f5ea41b92bc2727b87b63ef8a71ea1 (plain) (tree)





































































































































































































































































































































                                                                                                                                                                    
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

		}

	}

}