package heufybot.core; import heufybot.config.GlobalConfig.PasswordType; import heufybot.core.IRCServer.ConnectionState; import heufybot.core.cap.CAPException; import heufybot.core.cap.CapHandler; import heufybot.core.events.types.*; import heufybot.utils.StringUtils; import heufybot.utils.WhoisBuilder; import java.util.*; public class InputParser { private IRCServer server; private int nickSuffix; private boolean capEndSent; private List<CapHandler> finishedHandlers; private WhoisBuilder whoisBuilder; public InputParser(IRCServer server) { this.server = server; this.nickSuffix = 1; this.finishedHandlers = new ArrayList<CapHandler>(); this.whoisBuilder = new WhoisBuilder(); } public void parseLine(String line) { List<String> parsedLine = this.tokenizeLine(line); String senderInfo = ""; if (parsedLine.get(0).charAt(0) == ':') { senderInfo = parsedLine.remove(0); } String command = parsedLine.remove(0).toUpperCase(); if (command.equals("PING")) { this.server.cmdPONG(parsedLine.get(0)); this.server.getEventListenerManager().dispatchEvent( new PingEvent(this.server.getName(), parsedLine.get(0))); return; } else if (command.startsWith("ERROR")) { // Connection closed by server String errorMessage = parsedLine.get(0).toLowerCase(); this.server.getEventListenerManager().dispatchEvent( new ErrorEvent(this.server.getName(), parsedLine.get(0))); // I really need to clean up this piece of code some time :| if (errorMessage.contains("[killed:") || errorMessage.contains("lined") || errorMessage.contains("[quit:")) { this.server.disconnect(false); } else { this.server.disconnect(true); } return; } String sourceNick; String sourceLogin = ""; String sourceHostname = ""; String target = !parsedLine.isEmpty() ? parsedLine.get(0) : ""; if (target.startsWith(":")) { target = target.substring(1); } int exclamationPosition = senderInfo.indexOf("!"); int atPosition = senderInfo.indexOf("@"); if (senderInfo.startsWith(":")) { if (exclamationPosition > 0 && atPosition > 0 && exclamationPosition < atPosition) { // This is a command sourceNick = senderInfo.substring(1, exclamationPosition); sourceLogin = senderInfo.substring(exclamationPosition + 1, atPosition); sourceHostname = senderInfo.substring(atPosition + 1); } else { // This is probably a server response int code = StringUtils.tryParseInt(command); if (code != -1) { if (this.server.getConnectionState() != ConnectionState.Connected) { this.handleConnect(line, parsedLine, command); } else { this.handleServerResponse(line, parsedLine, command); } return; } else { // Could be a nick without a login or host sourceNick = senderInfo; } } } else { // No idea what this is, pass it to the CAP handlers if (this.server.getConnectionState() != ConnectionState.Connected) { for (CapHandler currentHandler : this.server.getConfig().getCapHandlers()) { if (currentHandler.handleUnknown(this.server, line)) { this.finishedHandlers.add(currentHandler); } } } return; } if (sourceNick.startsWith(":")) { sourceNick = sourceNick.substring(1); } if (this.server.getConnectionState() != ConnectionState.Connected && command.equals("CAP")) { this.handleConnect(line, parsedLine, command); } else { this.handleCommand(line, parsedLine, sourceNick, sourceLogin, sourceHostname, command, target); } } private void handleConnect(String line, List<String> parsedLine, String code) { String nickname = this.server.getConfig().getSettingWithDefault("nickname", "RE_HeufyBot"); String password = this.server.getConfig().getSettingWithDefault("password", ""); PasswordType passwordType = this.server.getConfig().getSettingWithDefault("passwordType", PasswordType.None); boolean autoNickChange = this.server.getConfig().getSettingWithDefault("autoNickChange", true); ArrayList<String> autoJoinChannels = this.server.getConfig().getSettingWithDefault( "autoJoinChannels", new ArrayList<String>()); if (code.equals("001")) { // 001 RPL_WELCOME this.server.setConnectionState(ConnectionState.Connected); this.server.setLoggedInNick(nickname + (this.nickSuffix == 1 ? "" : this.nickSuffix)); this.nickSuffix = 1; Logger.log("*** Logged onto server"); // Reset CAP stuff this.capEndSent = false; this.finishedHandlers.clear(); this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1))); if (passwordType == PasswordType.NickServPass) { this.server.nickservIdentify(password); } if (autoJoinChannels.size() > 0) { for (String channelName : autoJoinChannels) { String[] channel = channelName.split(" "); if (channel.length > 1) { // This channel has a key this.server.cmdJOIN(channel[0], channel[1]); } else { // This channel doesn't need a key this.server.cmdJOIN(channel[0], ""); } } } } else if (code.equals("433")) { // 443 ERR_NICKNAMEINUSE if (autoNickChange) { // Try a different nickname String usedNick = parsedLine.get(1); this.nickSuffix++; Logger.log("*** Nickname " + usedNick + " was already taken. Trying " + nickname + this.nickSuffix + "..."); this.server.cmdNICK(nickname + this.nickSuffix); } else { // Give up Logger.error("IRC Login", "Login failed. Nickname was already taken"); this.server.disconnect(false); } } else if (code.equals("451")) { // 451 ERR_NOTREGISTERED // The server does not support CAP. No action required } else if ((code.startsWith("4") || code.startsWith("5")) && !code.equals("439") && !code.equals("421")) { // 439 ERR_TARGETTOOFAST : No action required // Couldn't login. Disconnect. Logger.error("IRC Login", "Login failed."); this.server.disconnect(false); } else if (code.equals("CAP")) { String capCommand = parsedLine.get(1); List<String> capParams = Arrays.asList(parsedLine.get(2).split(" ")); if (capCommand.equals("LS")) { Logger.log("*** Supported capabilities: " + StringUtils.join(capParams, ", ")); for (CapHandler currentCapHandler : this.server.getConfig().getCapHandlers()) { try { if (currentCapHandler.handleLS(this.server, capParams)) { this.finishedHandlers.add(currentCapHandler); } } catch (CAPException e) { this.finishedHandlers.add(currentCapHandler); Logger.error("CAP Enabling", e.getMessage()); } } } else if (capCommand.equals("ACK")) { for (CapHandler currentCapHandler : this.server.getConfig().getCapHandlers()) { if (currentCapHandler.handleACK(this.server, capParams)) { this.finishedHandlers.add(currentCapHandler); } } } else if (capCommand.equals("NAK")) { for (CapHandler currentCapHandler : this.server.getConfig().getCapHandlers()) { try { if (currentCapHandler.handleNAK(this.server, capParams)) { this.finishedHandlers.add(currentCapHandler); } } catch (CAPException e) { Logger.error("CAP Enabling", e.getMessage()); } } } else { for (CapHandler currentHandler : this.server.getConfig().getCapHandlers()) { if (currentHandler.handleUnknown(this.server, line)) { this.finishedHandlers.add(currentHandler); } } } } else { for (CapHandler currentHandler : this.server.getConfig().getCapHandlers()) { if (currentHandler.handleUnknown(this.server, line)) { this.finishedHandlers.add(currentHandler); } } } if (!this.capEndSent && this.finishedHandlers.containsAll(this.server.getConfig().getCapHandlers())) { this.capEndSent = true; this.server.cmdCAP("END", ""); } } private void handleServerResponse(String rawResponse, List<String> parsedLine, String code) { if (code.equals("002") || code.equals("003")) { // 002 RPL_YOURHOST // 003 RPL_CREATED this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1))); } else if (code.equals("004")) { // 004 RPL_MYINFO this.server.getServerInfo().setServer(parsedLine.get(1)); this.server.getServerInfo().setServerVersion(parsedLine.get(2)); this.server.getServerInfo().getUserModes().clear(); for (int i = 0; i < parsedLine.get(3).length(); i++) { this.server.getServerInfo().getUserModes() .add(Character.toString(parsedLine.get(3).charAt(i))); } if (this.server.getServerInfo().getUserModes().contains("B")) { this.server.cmdMODE(this.server.getNickname(), "+B"); } this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), rawResponse.split(this.server .getNickname() + " ")[1])); } else if (code.equals("005")) { // 005 RPL_ISUPPORT // Server information. Might do something with this later. if (rawResponse.contains("PREFIX=")) { String prefixes = rawResponse.split("PREFIX=")[1]; prefixes = prefixes.substring(0, prefixes.indexOf(" ")); this.server.getServerInfo().setUserPrefixes(this.getUserPrefixes(prefixes)); this.server.getServerInfo().setReverseUserPrefixes( this.getReverseUserPrefixes(prefixes)); } if (rawResponse.contains("CHANTYPES=")) { String chantypes = rawResponse.split("CHANTYPES=")[1]; chantypes = chantypes.substring(0, chantypes.indexOf(" ")); this.server.getServerInfo().setChantypes(chantypes); } if (rawResponse.contains("CHANMODES=")) { String rawChanmodes = rawResponse.split("CHANMODES=")[1]; rawChanmodes = rawChanmodes.substring(0, rawChanmodes.indexOf(" ")); String[] chanmodes = rawChanmodes.split(","); this.server.getServerInfo().getChannelListModes().clear(); this.server.getServerInfo().getChannelNoArgsModes().clear(); this.server.getServerInfo().getChannelSetArgsModes().clear(); this.server.getServerInfo().getChannelSetUnsetArgsModes().clear(); for (int i = 0; i < chanmodes[0].length(); i++) { this.server.getServerInfo().getChannelListModes() .add(Character.toString(chanmodes[0].charAt(i))); } for (int i = 0; i < chanmodes[1].length(); i++) { this.server.getServerInfo().getChannelSetUnsetArgsModes() .add(Character.toString(chanmodes[1].charAt(i))); } for (int i = 0; i < chanmodes[2].length(); i++) { this.server.getServerInfo().getChannelSetArgsModes() .add(Character.toString(chanmodes[2].charAt(i))); } for (int i = 0; i < chanmodes[3].length(); i++) { this.server.getServerInfo().getChannelNoArgsModes() .add(Character.toString(chanmodes[3].charAt(i))); } } if (rawResponse.contains("NETWORK=")) { String network = rawResponse.split("NETWORK=")[1]; network = network.substring(0, network.indexOf(" ")); this.server.getServerInfo().setNetwork(network); } this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), rawResponse.split(this.server .getNickname() + " ")[1])); } else if (code.equals("042")) { // 042 RPL_YOURID // UUID, might do something with it later. Just log it for now. this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1) + " " + parsedLine.get(2))); } else if (code.equals("251") || code.equals("255") || code.equals("265") || code.equals("266")) { // 251 RPL_LUSERCLIENT // 255 RPL_LUSERME // 265 RPL_LOCALUSERS // 266 RPL_GLOBALUSERS this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1))); } else if (code.equals("252") || code.equals("254") || code.equals("396")) { // 252 RPL_LUSEROP // 254 RPL_LUSERCHANNELS // 396 RPL_HOSTHIDDEN this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1) + " " + parsedLine.get(2))); } else if (code.equals("311")) { // 311 RPL_WHOISUSER this.whoisBuilder.setNickname(parsedLine.get(1)); this.whoisBuilder.setLogin(parsedLine.get(2)); this.whoisBuilder.setHostname(parsedLine.get(3)); this.whoisBuilder.setRealname(parsedLine.get(5)); } else if (code.equals("312")) { // 312 RPL_WHOISSERVER this.whoisBuilder.setServer(parsedLine.get(2)); this.whoisBuilder.setServerInfo(parsedLine.get(3)); } else if (code.equals("313")) { // 313 RPL_WHOISOPERATOR this.whoisBuilder.setOperPrivs(parsedLine.get(2)); } else if (code.equals("315")) { // 315 RPL_ENDOFWHO // No action required } else if (code.equals("317")) { // 317 RPL_WHOISIDLE this.whoisBuilder.setIdleSeconds(StringUtils.tryParseLong(parsedLine.get(2))); // Sign-on time is not in the RFC, but most deamons support this // nowadays anyway this.whoisBuilder.setSignOnTime(StringUtils.tryParseLong(parsedLine.get(3))); } else if (code.equals("318")) { // 318 RPL_ENDOFWHOIS this.server.getEventListenerManager().dispatchEvent( new WhoisEvent(this.server.getName(), this.whoisBuilder)); this.whoisBuilder = new WhoisBuilder(); } else if (code.equals("319")) { // 319 RPL_WHOISCHANNELS this.whoisBuilder.setChannels(StringUtils.parseStringtoList(parsedLine.get(2), " ")); } else if (code.equals("324")) { // 324 RPL_CHANNELMODEIS IRCChannel channel = this.server.getChannel(parsedLine.get(1)); this.handleMode(channel.getName(), parsedLine.get(2)); this.server.getEventListenerManager().dispatchEvent( new ServerResponseChannelEvent(this.server.getName(), channel, "Channel modes currently set: " + parsedLine.get(2))); } else if (code.equals("329")) { // 329 RPL_CREATIONTIME IRCChannel channel = this.server.getChannel(parsedLine.get(1)); this.server .getEventListenerManager() .dispatchEvent( new ServerResponseChannelEvent( this.server.getName(), channel, "Channel was created on " + new Date( StringUtils.tryParseLong(parsedLine.get(2)) * 1000))); } else if (code.equals("332")) { // 332 RPL_TOPIC IRCChannel channel = this.server.getChannel(parsedLine.get(1)); channel.setTopic(parsedLine.get(2)); this.server.getEventListenerManager().dispatchEvent( new ServerResponseChannelEvent(this.server.getName(), channel, "Topic is \'" + parsedLine.get(2) + "\'")); } else if (code.equals("333")) { // 333 RPL_TOPICWHOTIME IRCChannel channel = this.server.getChannel(parsedLine.get(1)); channel.setTopicSetter(parsedLine.get(2)); long topicTimestamp = StringUtils.tryParseLong(parsedLine.get(3)); channel.setTopicSetTimestamp(topicTimestamp); this.server.getEventListenerManager().dispatchEvent( new ServerResponseChannelEvent(this.server.getName(), channel, "Set by " + parsedLine.get(2) + " on " + new Date(topicTimestamp * 1000))); } else if (code.equals("352")) { // 352 RPL_WHOREPLY IRCChannel channel = this.server.getChannel(parsedLine.get(1)); IRCUser user = this.server.getUser(parsedLine.get(5)); user.setLogin(parsedLine.get(2)); user.setHostname(parsedLine.get(3)); user.setServer(parsedLine.get(4)); if (channel.getUser(user.getNickname()) == null) { channel.addUser(user); } List<String> flags = StringUtils.parseStringtoList(parsedLine.get(6), ""); flags.remove(0); if (flags.remove(0).equals("G")) { user.setAway(true); } if (flags.size() > 0 && flags.remove(0).equals("*")) { user.setOper(true); } String modes = StringUtils.join(flags, ""); for (int i = 0; i < modes.length(); i++) { String mode = this.server.getServerInfo().getReverseUserPrefixes() .get(Character.toString(modes.charAt(i))); channel.parseModeChangeOnUser(user, "+" + mode); } String rawEnd = parsedLine.get(7); user.setHops(StringUtils.tryParseInt(rawEnd.substring(0, rawEnd.indexOf(" ")))); user.setRealname(rawEnd.substring(rawEnd.indexOf(" ") + 1)); } else if (code.equals("353")) { // 353 RPL_NAMREPLY IRCChannel channel = this.server.getChannel(parsedLine.get(2)); String[] users = parsedLine.get(3).split(" "); for (String user2 : users) { String prefixes = ""; String nickname = user2; LinkedHashMap<String, String> reverseUserPrefixes = this.server.getServerInfo() .getReverseUserPrefixes(); for (int j = 0; j < reverseUserPrefixes.size(); j++) { String firstCharString = "" + nickname.charAt(0); if (reverseUserPrefixes.containsKey(firstCharString)) { prefixes += reverseUserPrefixes.get(firstCharString); nickname = nickname.substring(1); } } IRCUser user = this.server.getUser(nickname); if (channel.getUser(user.getNickname()) == null) { channel.addUser(user); } channel.parseModeChangeOnUser(user, "+" + prefixes); } } else if (code.equals("366")) { // 366 RPL_ENDOFNAMES // No action required } else if (code.equals("372")) { // 372 RPL_MOTD this.server.getServerInfo().appendMotd(parsedLine.get(1) + "\n"); this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1))); } else if (code.equals("375")) { // 375 RPL_MOTDSTART this.server.getServerInfo().setMotd(""); this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1))); } else if (code.equals("376")) { // 376 RPL_ENDOFMOTD this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), parsedLine.get(1))); } else { // Not parsed (yet) this.server.getEventListenerManager().dispatchEvent( new ServerResponseEvent(this.server.getName(), "(" + code + ") " + rawResponse.substring(rawResponse.indexOf(this.server.getNickname() + " ") + this.server.getNickname().length() + 1))); } } private void handleCommand(String line, List<String> parsedLine, String sourceNick, String sourceLogin, String sourceHostname, String command, String target) { IRCUser source = this.server.getUser(sourceNick); IRCChannel channel = this.server.getChannel(target); String message = parsedLine.size() >= 2 ? parsedLine.get(1) : ""; if (command.equals("PRIVMSG") && message.startsWith("\u0001") && message.endsWith("\u0001")) { // Message is a CTCP request String request = message.substring(1, message.length() - 1); if (request.toUpperCase().startsWith("ACTION ") && channel != null) { // ACTION request this.server.getEventListenerManager().dispatchEvent( new ActionEvent(this.server.getName(), source, channel, request .substring(7))); } else if (request.toUpperCase().startsWith("ACTION ")) { // ACTION request in a PM // We don't this user yet, so send a WHOIS this.server.cmdWHOIS(sourceNick); this.server.getEventListenerManager().dispatchEvent( new PMActionEvent(this.server.getName(), source, request.substring(7))); } else if (request.toUpperCase().startsWith("PING ")) { this.server.ctcpReply(sourceNick, "PING", request.substring(5)); this.server.getEventListenerManager().dispatchEvent( new CTCPRequestEvent(this.server.getName(), source, "PING")); } else if (request.toUpperCase().equals("VERSION")) { this.server.ctcpReply( sourceNick, "VERSION", "RE_HeufyBot V" + HeufyBot.VERSION + ", OS: " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ")," + System.getProperty("os.arch")); this.server.getEventListenerManager().dispatchEvent( new CTCPRequestEvent(this.server.getName(), source, "VERSION")); } else if (request.toUpperCase().equals("TIME")) { this.server.ctcpReply(sourceNick, "TIME", new Date().toString()); this.server.getEventListenerManager().dispatchEvent( new CTCPRequestEvent(this.server.getName(), source, "TIME")); } else if (request.toUpperCase().equals("FINGER")) { this.server.ctcpReply(sourceNick, "FINGER", "Why would you finger a bot?!"); this.server.getEventListenerManager().dispatchEvent( new CTCPRequestEvent(this.server.getName(), source, "FINGER")); } } else if (command.equals("PRIVMSG") && channel != null) { // Message to the channel this.server.getEventListenerManager().dispatchEvent( new MessageEvent(this.server.getName(), source, channel, message)); } else if (command.equals("PRIVMSG")) { // Private message if (source.getHostname().equals("")) { // We don't this user yet, so send a WHOIS this.server.cmdWHOIS(sourceNick); } this.server.getEventListenerManager().dispatchEvent( new PMMessageEvent(this.server.getName(), source, message)); } else if (command.equals("JOIN")) { // Someone joins the channel if (sourceNick.equalsIgnoreCase(this.server.getNickname())) { // The bot is joining the channel, do setup channel = new IRCChannel(target); this.server.getChannels().add(channel); this.server.cmdWHO(channel.getName()); this.server.cmdMODE(channel.getName(), ""); source = new IRCUser(sourceNick, sourceLogin, sourceHostname); } else { // Someone else is joining the channel source.setLogin(sourceLogin); source.setHostname(sourceHostname); channel.addUser(source); } this.server.getEventListenerManager().dispatchEvent( new JoinEvent(this.server.getName(), source, channel)); } else if (command.equals("PART")) { if (sourceNick.equalsIgnoreCase(this.server.getNickname())) { // The bot is leaving the channel this.server.getChannels().remove(channel); } else { // Someone else is leaving the channel this.server.getChannel(target).removeUser(source); } boolean noCommonChannels = true; for (IRCChannel channel2 : this.server.getChannels()) { if (channel2.getUser(sourceNick) != null) { noCommonChannels = false; } } if (noCommonChannels) { this.server.getUsers().remove(source); } this.server.getEventListenerManager().dispatchEvent( new PartEvent(this.server.getName(), source, channel, message)); } else if (command.equals("NICK")) { // Someone is changing their nick String newNick = target; for (IRCChannel channel2 : this.server.getChannels()) { IRCUser user = channel2.getUser(sourceNick); if (user != null) { user.setNickname(newNick); } } if (sourceNick.equalsIgnoreCase(this.server.getNickname())) { // The bot's nick is changed this.server.setLoggedInNick(newNick); } this.server.getEventListenerManager().dispatchEvent( new NickChangeEvent(this.server.getName(), source, newNick, sourceNick)); } else if (command.equals("NOTICE")) { // Someone sent a notice if (channel == null) { this.server.getEventListenerManager().dispatchEvent( new NoticeEvent(this.server.getName(), sourceNick, message)); } else { this.server.getEventListenerManager() .dispatchEvent( new ChannelNoticeEvent(this.server.getName(), sourceNick, channel, message)); } } else if (command.equals("QUIT")) { // Someone quit the server if (!sourceNick.equalsIgnoreCase(this.server.getNickname())) { this.server.getEventListenerManager().dispatchEvent( new QuitEvent(this.server.getName(), source, target)); for (IRCChannel channel2 : this.server.getChannels()) { if (channel2.getUser(sourceNick) != null) { channel2.removeUser(source); } } // Remove the user who quit from the users list this.server.getUsers().remove(source); } else { // The bot disconnected this.server.disconnect(false); } } else if (command.equals("KICK")) { // Someone is being kicked IRCUser recipient = this.server.getUser(message); if (message.equalsIgnoreCase(this.server.getNickname())) { // The bot just got kicked this.server.getChannels().remove(channel); } else { // Someone else got kicked channel.removeUser(recipient); } this.server.getEventListenerManager().dispatchEvent( new KickEvent(this.server.getName(), recipient, source, channel, parsedLine .get(2))); } else if (command.equals("MODE")) { // A mode is being set String mode = line.substring(line.indexOf(target, 2) + target.length() + 1); if (mode.startsWith(":")) { mode = mode.substring(1); } if (sourceNick.equals(target)) { List<String> modeToParse = StringUtils.parseStringtoList(mode, " "); mode = modeToParse.get(modeToParse.size() - 1); } this.server.getEventListenerManager().dispatchEvent( new ModeEvent(this.server.getName(), sourceNick, channel, mode)); this.handleMode(target, mode); } else if (command.equals("TOPIC")) { // Someone is changing the topic long currentTime = System.currentTimeMillis(); channel.setTopic(message); channel.setTopicSetter(sourceNick); channel.setTopicSetTimestamp(currentTime); this.server.getEventListenerManager().dispatchEvent( new TopicEvent(this.server.getName(), sourceNick, channel, message)); } else if (command.equals("INVITE")) { // Someone is inviting someone into the channel if (target.equalsIgnoreCase(this.server.getNickname())) { // The bot is being invited. Join the channel. this.server.cmdJOIN(message, ""); } this.server.getEventListenerManager().dispatchEvent( new InviteEvent(this.server.getName(), source, target, message)); } } private void handleMode(String target, String mode) { if (target.equals(this.server.getNickname())) { // This is a user mode. this.server.parseUserModesChange(mode); return; } else { IRCChannel channel = this.server.getChannel(target); if (!mode.contains(" ")) { // This mode change does not contain any arguments channel.parseModeChange(mode); return; } else { // This mode change contains arguments. Handle them one by one. List<String> params = StringUtils.parseStringtoList(mode, " "); char modeOperator = '+'; int paramNumber = 1; for (int i = 0; i < params.get(0).length(); i++) { char atPosition = params.get(0).charAt(i); if (atPosition == '+' || atPosition == '-') { modeOperator = atPosition; } else if (this.server.getServerInfo().getUserPrefixes() .containsKey(Character.toString(atPosition))) { // This mode is changing a user's access level in the // channel IRCUser user = channel.getUser(params.get(paramNumber)); channel.parseModeChangeOnUser(user, "" + modeOperator + atPosition); paramNumber++; } else if (this.server.getServerInfo().getChannelListModes() .contains(Character.toString(atPosition))) { // This mode is a list mode. It's not important to us. // Skip to the next argument. paramNumber++; } else if (this.server.getServerInfo().getChannelSetArgsModes() .contains(Character.toString(atPosition))) { // This mode needs an argument to be set. Handle it in // the channel. if (modeOperator == '+') { channel.getModes().put(Character.toString(atPosition), params.get(paramNumber)); } else { channel.getModes().remove(Character.toString(atPosition)); } paramNumber++; } else if (this.server.getServerInfo().getChannelSetUnsetArgsModes() .contains(Character.toString(atPosition))) { // This mode needs an argument to be set AND to be // unset. Handle it in the channel. if (modeOperator == '+') { channel.getModes().put(Character.toString(atPosition), params.get(paramNumber)); } else { channel.getModes().remove(Character.toString(atPosition)); } paramNumber++; } else if (this.server.getServerInfo().getChannelNoArgsModes() .contains(Character.toString(atPosition))) { // This mode doesn't take arguments. Parse it normally. channel.parseModeChange("" + modeOperator + atPosition); } } } } } private List<String> tokenizeLine(String input) { List<String> retn = new ArrayList<String>(); if (input == null || input.length() == 0) { return retn; } String temp = input; while (true) { if (temp.startsWith(":") && retn.size() > 0) { retn.add(temp.substring(1)); return retn; } String[] split = temp.split(" ", 2); retn.add(split[0]); if (split.length > 1) { temp = split[1]; } else { break; } } return retn; } private LinkedHashMap<String, String> getUserPrefixes(String prefixString) { LinkedHashMap<String, String> prefixes = new LinkedHashMap<String, String>(); char[] channelModes = prefixString.substring(1, prefixString.indexOf(")")).toCharArray(); char[] userLevels = prefixString.substring(prefixString.indexOf(")") + 1).toCharArray(); for (int i = 0; i < channelModes.length; i++) { prefixes.put("" + channelModes[i], "" + userLevels[i]); } return prefixes; } private LinkedHashMap<String, String> getReverseUserPrefixes(String prefixString) { LinkedHashMap<String, String> prefixes = new LinkedHashMap<String, String>(); char[] channelModes = prefixString.substring(1, prefixString.indexOf(")")).toCharArray(); char[] userLevels = prefixString.substring(prefixString.indexOf(")") + 1).toCharArray(); for (int i = 0; i < channelModes.length; i++) { prefixes.put("" + userLevels[i], "" + channelModes[i]); } return prefixes; } }