package de.skuzzle.polly.core.eventhandler; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import de.skuzzle.polly.core.internal.DefaultUserAttributesProvider; import de.skuzzle.polly.core.internal.irc.IrcManagerImpl; import de.skuzzle.polly.core.internal.users.UserManagerImpl; import de.skuzzle.polly.sdk.AbstractDisposable; import de.skuzzle.polly.sdk.Types.BooleanType; import de.skuzzle.polly.sdk.User; import de.skuzzle.polly.sdk.eventlistener.ConnectionEvent; import de.skuzzle.polly.sdk.eventlistener.ConnectionListener; import de.skuzzle.polly.sdk.eventlistener.MessageEvent; import de.skuzzle.polly.sdk.eventlistener.MessageListener; import de.skuzzle.polly.sdk.eventlistener.NickChangeEvent; import de.skuzzle.polly.sdk.eventlistener.NickChangeListener; import de.skuzzle.polly.sdk.eventlistener.SpotEvent; import de.skuzzle.polly.sdk.eventlistener.UserEvent; import de.skuzzle.polly.sdk.eventlistener.UserListener; import de.skuzzle.polly.sdk.eventlistener.UserSpottedListener; import de.skuzzle.polly.sdk.exceptions.AlreadySignedOnException; import de.skuzzle.polly.sdk.exceptions.DisposingException; import de.skuzzle.polly.sdk.exceptions.UnknownUserException; import de.skuzzle.polly.tools.concurrent.ThreadFactoryBuilder; public class AutoLogonHandler extends AbstractDisposable implements UserSpottedListener, NickChangeListener, UserListener, ConnectionListener { private class AutoLogonRunnable implements Runnable { private final String forUser; private volatile boolean canceled; public AutoLogonRunnable(String forUser) { this.forUser = forUser; } public void cancel() { this.canceled = true; } @Override public void run() { if (this.canceled) { return; } // ISSUE 0000091: request status for the user from nickserv requestAuthStatus(this.forUser); } } private final MessageListener autoSignOnHandler = new MessageListener() { private void process(MessageEvent e) { try { provider.processMessageEvent(e, userManager); } catch (AlreadySignedOnException e1) { logger.trace("User logged in while waiting for auto logon"); //$NON-NLS-1$ } catch (UnknownUserException e1) { logger.error("Error while auto logon. User '" + e1.getMessage() + //$NON-NLS-1$ "' unknown", e1); //$NON-NLS-1$ } } @Override public void noticeMessage(MessageEvent e) { this.process(e); } @Override public void publicMessage(MessageEvent e) { this.process(e); } @Override public void privateMessage(MessageEvent e) { this.process(e); } @Override public void actionMessage(MessageEvent e) { this.process(e); } }; private static Logger logger = Logger.getLogger(AutoLogonHandler.class.getName()); private final IrcManagerImpl ircManager; private final UserManagerImpl userManager; private final ScheduledExecutorService autoLogonExecutor; private final Map<String, AutoLogonRunnable> scheduledLogons; private final int autoLoginDelay; private final AutoLoginProvider provider; public AutoLogonHandler(IrcManagerImpl ircManager, UserManagerImpl userManager, AutoLoginProvider provider, int autoLoginDelay) { ircManager.addMessageListener(this.autoSignOnHandler); this.provider = provider; this.ircManager = ircManager; this.userManager = userManager; this.autoLogonExecutor = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder("LOGON")); //$NON-NLS-1$ this.scheduledLogons = new HashMap<String, AutoLogonRunnable>(); this.autoLoginDelay = autoLoginDelay; } @Override public void userSpotted(SpotEvent e) { final String forUser = e.getUser().getNickName(); if (this.userExists(forUser)) { // try instant login if user just spotted this.provider.requestAuthentification(forUser, this.ircManager); } this.scheduleAutoLogon(forUser); } @Override public void nickChanged(NickChangeEvent e) { /* * If there is a auto logon scheduled for the old nickname, it will be * canceled. If there is a registered user with the new nickname that is * currently not logged on, a new auto logon is scheduled for that user. */ synchronized (this.scheduledLogons) { final AutoLogonRunnable alr = this.scheduledLogons.get( e.getOldUser().getNickName()); if (alr != null) { alr.cancel(); this.scheduledLogons.remove(e.getOldUser().getNickName()); logger.debug("Auto logon for " + e.getOldUser() + " canceled"); //$NON-NLS-1$//$NON-NLS-2$ } final User u = this.userManager.getUser(e.getNewUser().getNickName()); if (u != null && !this.userManager.isSignedOn(u)) { this.scheduleAutoLogon(e.getNewUser().getNickName()); } } } private void requestAuthStatus(String forUser) { synchronized (this.scheduledLogons) { if (this.scheduledLogons.containsKey(forUser)) { this.scheduledLogons.remove(forUser); this.provider.requestAuthentification(forUser, this.ircManager); } } } private void scheduleAutoLogon(String forUser) { synchronized (this.scheduledLogons) { if (this.scheduledLogons.containsKey(forUser)) { return; } if (this.userExists(forUser)) { final AutoLogonRunnable runMe = new AutoLogonRunnable(forUser); this.scheduledLogons.put(forUser, runMe); this.autoLogonExecutor.schedule(runMe, this.autoLoginDelay, TimeUnit.MILLISECONDS); logger.debug("Auto logon for " + forUser + " scheduled"); //$NON-NLS-1$ //$NON-NLS-2$ } } } private boolean userExists(String name) { final User user = this.userManager.getUser(name); if (user == null) { return false; } final BooleanType bool = (BooleanType) user.getAttribute(DefaultUserAttributesProvider.AUTO_LOGON); return bool.getValue(); } @Override public void userSignedOn(UserEvent e) { synchronized (this.scheduledLogons) { final AutoLogonRunnable alr = this.scheduledLogons.get(e.getUser().getName()); if (alr != null) { logger.trace("Removing scheduled auto logon for user " + //$NON-NLS-1$ e.getUser().getName()); alr.cancel(); this.scheduledLogons.remove(e.getUser().getName()); } } } @Override protected void actualDispose() throws DisposingException { this.autoLogonExecutor.shutdown(); try { this.autoLogonExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS); } catch (InterruptedException ignore) {} } @Override public void userSignedOff(UserEvent ignore) {} @Override public void userLost(SpotEvent ignore) {} @Override public void ircConnectionEstablished(ConnectionEvent e) {} @Override public void ircConnectionLost(ConnectionEvent e) {} }