package com.esri.geoevent.solutions.transport.irc.jerklib; import java.util.*; /** * This class allows you to probe various pieces of information about the server. * For more information see <a href="http://tools.ietf.org/draft/draft-hardy-irc-isupport/draft-hardy-irc-isupport-00.txt"> * this</a> and <a href="http://www.silen.eu.org/usr/irc/005.html">this</a>. These documents will explain all the pieces of information * provided through this class. * * @author <a href="mailto:mohadib@openactive.org">Jason Davis</a> */ public class ServerInformation { private String caseMapping = "", ircd = "", serverName = ""; private String[] channelPrefixes, statusPrefixes, channelModes; private int maxChanNameLen, maxModesPerCommand, maxNickLen, maxSilenceListSize, maxTopicLen, maxAwayLen, maxKickLen, maxKeyLen, maxHostLen, maxUserLen; private boolean supportsCNotice, supportsCPrivMsg, supportsBanExceptions, supportsInviteExceptions; private boolean supportsSafeList, supportsStatusNotice, supportsCAPAB, supportsNickPrefixes, supportsSilenceList; private boolean supportsKnock, supportsWhox, supportsWallchops, supportsWallVoices, supportsUserIP, supportsEtrace; private Map<String, Integer> joinLimits = new HashMap<String, Integer>(); private Map<String, String> nickPrefixMap = new LinkedHashMap<String, String>(); private Map<String, ModeType> modeMap = new HashMap<String, ModeType>(); /** * <p> * <ul> * <li> Type A: Modes that must add or remove an address to or from a list. * These modes <b>MUST</b> always have a parameter when sent from the server to a client. * A client MAY issue the mode without an argment to obtain the current contents of the list. * </li> * <li> Type B: modes that change a setting on a channel. These modes <b>MUST</b> always have a parameter,</li> * <p/> * <li>Type C: modes that change a setting on a channel. These modes <b>MUST</b> have a parameter when being set, and <b>MUST NOT</b> * and mUST NOT have a parameter when being unset. * </li> * <li> Type D: Modes that change a setting on a channel. These modes <b>MUST</b> NOT have a parameter.</li> * </ul> * </p> * . */ public static enum ModeType { GROUP_A, GROUP_B, GROUP_C, GROUP_D, CUSTOM, ALL } public ServerInformation() { //init some defaults because some servers either dont send 005 //or some events that need serverinformation to parse might happen before 005 channelPrefixes = new String[]{"#" , "&" , "!" , "+"}; nickPrefixMap.put("@", "o"); nickPrefixMap.put("%", "h"); nickPrefixMap.put("+", "v"); } /* * :irc.nixgeeks.com 005 mohadib CMDS=KNOCK,MAP,DCCALLOW,USERIP SAFELIST HCN MAXCHANNELS=20 CHANLIMIT=#:20 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 MAXTARGETS=20 WALLCHOPS :are supported by this server * :irc.nixgeeks.com 005 mohadib WATCH=128 SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beI,kfL,lj,psmntirRcOAQKVGCuzNSMTG NETWORK=NixGeeks CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT STATUSMSG=~&@%+ EXCEPTS INVEX :are supported by this server * :swiftco.wa.us.dal.net 005 r0bby_ NETWORK=DALnet SAFELIST MAXBANS=200 MAXCHANNELS=20 CHANNELLEN=32 KICKLEN=307 NICKLEN=30 TOPICLEN=307 MODES=6 CHANTYPES=# CHANLIMIT=#:20 PREFIX=(ov)@+ STATUSMSG=@+ :are available on this server * :swiftco.wa.us.dal.net 005 r0bby_ CASEMAPPING=ascii WATCH=128 SILENCE=10 ELIST=cmntu EXCEPTS INVEX CHANMODES=beI,k,jl,cimMnOprRst MAXLIST=b:200,e:100,I:100 TARGMAX=DCCALLOW:,JOIN:,KICK:4,KILL:20,NOTICE:20,PART:,PRIVMSG:20,WHOIS:,WHOWAS: :are available on this server * :Vancouver.BC.CA.Undernet.org 005 r0bby___ MAXNICKLEN=15 TOPICLEN=160 AWAYLEN=160 KICKLEN=160 CHANNELLEN=200 MAXCHANNELLEN=200 CHANTYPES=#& PREFIX=(ov)@+ STATUSMSG=@+ CHANMODES=b,k,l,imnpstrDd CASEMAPPING=rfc1459 NETWORK=UnderNet :are supported by this server * :Vancouver.BC.CA.Undernet.org 005 r0bby___ WHOX WALLCHOPS WALLVOICES USERIP CPRIVMSG CNOTICE SILENCE=15 MODES=6 MAXCHANNELS=10 MAXBANS=45 NICKLEN=12 :are supported by this server * :kubrick.freenode.net 005 BigDaddy IRCD=dancer CAPAB CHANTYPES=# EXCEPTS INVEX CHANMODES=bdeIq,k,lfJD,cgijLmnPQrRstz CHANLIMIT=#:20 PREFIX=(ov)@+ MAXLIST=bdeI:50 MODES=4 STATUSMSG=@ KNOCK NICKLEN=16 :are supported by this server * :kubrick.freenode.net 005 BigDaddy SAFELIST CASEMAPPING=ascii CHANNELLEN=30 TOPICLEN=450 KICKLEN=450 KEYLEN=23 USERLEN=10 HOSTLEN=63 SILENCE=50 :are supported by this server */ public void parseServerInfo(String rawData) { String[] tokens = rawData.split("\\s+"); serverName = tokens[0].substring(1); for (int i = 3; i < tokens.length; i++) { String[] subTokens = tokens[i].split("="); if (subTokens[0].equals("IRCD")) { ircd = subTokens[1]; } else if (subTokens[0].equals(" ")) { supportsCAPAB = true; } else if (subTokens[0].equals("CHANTYPES") && subTokens.length == 2) { String[] data = subTokens[1].split(""); channelPrefixes = new String[data.length - 1]; System.arraycopy(data, 1, channelPrefixes, 0, data.length - 1); } else if (subTokens[0].equals("EXCEPTS")) { supportsBanExceptions = true; } else if (subTokens[0].equals("INVEX")) { supportsInviteExceptions = true; } else if (subTokens[0].equals("SAFELIST")) { supportsSafeList = true; } else if (subTokens[0].equals("CASEMAPPING")) { caseMapping = subTokens[1]; } else if (subTokens[0].equals("CHANNELLEN")) { maxChanNameLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("MAXCHANNELLEN")) { maxChanNameLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("TOPICLEN")) { maxTopicLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("AWAYLEN")) { maxAwayLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("NICKLEN")) { maxNickLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("MAXNICKLEN")) { maxNickLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("KICKLEN")) { maxKickLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("KEYLEN")) { maxKeyLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("HOSTLEN")) { maxHostLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("USERLEN")) { maxUserLen = Integer.parseInt(subTokens[1]); } else if (subTokens[0].equals("CNOTICE")) { supportsCNotice = true; } else if (subTokens[0].equals("CPRIVMSG")) { supportsCPrivMsg = true; } else if (subTokens[0].equals("KNOCK")) { supportsKnock = true; } else if (subTokens[0].equals("WHOX")) { supportsWhox = true; } else if (subTokens[0].equals("WALLCHOPS")) { supportsWallchops = true; } else if (subTokens[0].equals("WALLVOICES")) { supportsWallVoices = true; } else if (subTokens[0].equals("USERIP")) { supportsUserIP = true; } else if (subTokens[0].equals("ETRACE")) { supportsEtrace = true; } else if (subTokens[0].equals("SILENCE")) { if (subTokens.length == 2) { supportsSilenceList = true; maxSilenceListSize = Integer.parseInt(subTokens[1]); } else { supportsSilenceList = false; } } else if (subTokens[0].equals("CHANLIMIT")) { String[] keyVals = subTokens[1].split(","); for (String keyVal : keyVals) { String[] limits = keyVal.split(":"); String[] chanPrefixes = limits[0].split(""); int limit = -1; if (limits.length == 2) { limit = Integer.parseInt(limits[1]); } for (String chanPrefix : chanPrefixes) { if (chanPrefix.matches("")) { continue; } joinLimits.put(chanPrefix, limit); } } } else if (subTokens[0].equals("PREFIX")) { nickPrefixMap.clear(); if (subTokens.length == 2) { supportsNickPrefixes = true; String[] modesAndPrefixes = subTokens[1].split("\\)"); modesAndPrefixes[0] = modesAndPrefixes[0].substring(1); String[] modes = modesAndPrefixes[0].split(""); String[] prefixes = modesAndPrefixes[1].split(""); for (int x = 0; x < prefixes.length; x++) { if (prefixes[x].matches("")) { continue; } nickPrefixMap.put(prefixes[x], modes[x]); modeMap.put(modes[x], ModeType.GROUP_B); } } } else if (subTokens[0].equals("MODES")) { if (subTokens.length == 2) { maxModesPerCommand = Integer.parseInt(subTokens[1]); } } else if (subTokens[0].equals("STATUSMSG")) { supportsStatusNotice = true; String[] tmp = subTokens[1].split(""); statusPrefixes = new String[tmp.length - 1]; System.arraycopy(tmp, 1, statusPrefixes, 0, tmp.length - 1); } else if (subTokens[0].equals("CHANMODES")) { String[] modeGroups = subTokens[1].split(","); for (int x = 0; x < modeGroups.length; x++) { ModeType mt = ModeType.CUSTOM; switch (x) { case 0: mt = ModeType.GROUP_A; break; case 1: mt = ModeType.GROUP_B; break; case 2: mt = ModeType.GROUP_C; break; case 3: mt = ModeType.GROUP_D; break; } String[] modes = modeGroups[x].split(""); for (String mode : modes) { if (mode.equals("")) { continue; } modeMap.put(mode, mt); } } channelModes = modeMap.keySet().toArray(new String[modeMap.size()]); } else { //System.err.println("Unreconized Server Info Token:" + subTokens[0]); } } } /** * Get all modes of the given {@link ModeType}. * * @param type - the mode type * @return an array containing all modes of the given type. * @see ModeType */ public String[] getModes(ModeType type) { List<String> modesList = new ArrayList<String>(); for (String key : modeMap.keySet()) { if (modeMap.get(key) == type || type == ModeType.ALL) { modesList.add(key); } } return modesList.toArray(new String[modesList.size()]); } /** * Get the {@link ModeType} for the given mode. * * @param mode the mode * @return the {@link ModeType}. * @see ModeType */ public ModeType getTypeForMode(String mode) { return modeMap.get(mode); } /** * Get the server name * * @return the name of the server */ public String getServerName() { return serverName; } /** * Get the IRCD String (this will usually be the name of the ircd. * * @return the IRCD string. */ public String getIrcdString() { return ircd; } /** * Get the case mapping used by this server.<br> * Will be one of the following: ascii, rfc1459, or strict-rfc1459 * * @return the case mapping used by the server */ public String getCaseMapping() { return caseMapping; } /** * Used to indicate the maximum amount of channels that a client may join * of a given prefix. A commen example prefix would be # * * @param prefix the channel prefix * @return the maximum join limit for the given channel prefix */ public int getChannelJoinLimitForPrefix(String prefix) { return joinLimits.get(prefix); } /** * Retrieve all supported channel modes. * * @return the supported channel modes */ public String[] getSupportedChannelModes() { return channelModes; } /** * Get whether or not the server supports CAPAB * * @return whether or not the server support CAPAB */ public boolean supportsCAPAB() { return supportsCAPAB; } /** * Get whether or not CNOTICE are supported by the server. * * @return whether or not CNOTICE is supported by the server */ public boolean supportsCNotice() { return supportsCNotice; } /** * Get whether or not the server supports CPRIVMSG * * @return whether or not the server supports CPRIVMSG */ public boolean supportsCPrivMsg() { return supportsCPrivMsg; } /** * Get whether or not WHOX is supported, * * @return whether or not WHOX is supported. */ public boolean supportsWhox() { return supportsWhox; } /** * Get if WALLCHOPS is supported. * * @return whether or not WALLCHOPS is supported. */ public boolean supportsWallChops() { return supportsWallchops; } /** * Get whether or not WALLVOICES is supported. * * @return whether or not WALLVOICES is supported. */ public boolean supportsWallVoices() { return supportsWallVoices; } /** * Get whether or not the server supports ban exceptions * * @return whether or not server supports ban exceptions. */ public boolean supportsBanExceptions() { return supportsBanExceptions; } /** * Get whether or not Invite Exceptions are supported. * * @return whether or not invite are supported. */ public boolean supportsInviteExceptions() { return supportsInviteExceptions; } /** * Get whether or not KNOCK is supported. * * @return whether or not KNOCK is supported */ public boolean supportsKnock() { return supportsKnock; } /** * Get Whether or not USERIP is supported. * * @return whether or not USERIP is supported. */ public boolean supportsUserIp() { return supportsUserIP; } /** * Get whether or not ETRACE is supported. * * @return whether or not ETRACE is supported. */ public boolean supportsEtrace() { return supportsEtrace; } /** * Get whether or not SAFELIST is supported. * * @return whether or not SAFELIST is supported. */ public boolean supportsSafeList() { return supportsSafeList; } /** * Whether or not SILENCE list is supported * * @return whether or not the SILENCE list is supported */ public boolean supportsSilenceList() { return supportsSilenceList; } /** * Get Whether or not nick prefixes are supported. * * @return whether or not nick prefixes are supported. */ public boolean supportsNickPrefixes() { return supportsNickPrefixes; } /** * Get whether or not status notices are supported. * * @return whether or not status notices are supported. */ public boolean supportsStatusNotices() { return supportsStatusNotice; } /** * Get the maximum number of modes per command * * @return Maximum of number of modes per command. */ public int getMaxModesPerCommnad() { return maxModesPerCommand; } /** * Get the maximum away message length * * @return max away message length */ public int getMaxAwayLength() { return maxAwayLen; } /** * Get max kick message length * * @return get the max kick msg length */ public int getMaxKickLength() { return maxKickLen; } /** * Get the max nick length. * * @return max nick length */ public int getMaxNickLength() { return maxNickLen; } /** * Get the maximum silence list length * * @return max length of the silence list */ public int getMaxSilenceListSize() { return maxSilenceListSize; } /** * Get the max topic length * * @return max topic length */ public int getMaxTopicLength() { return maxTopicLen; } /** * Get the max channel Name Lengt * * @return max channel name length */ public int getMaxChannelNameLength() { return maxChanNameLen; } /** * Get the max channel key length * * @return max channel key length */ public int getMaxKeyLength() { return maxKeyLen; } /** * Get the maximum Hostname length * * @return max hostname length */ public int getMaxHostLength() { return maxHostLen; } /** * Get the max username length * * @return max username length */ public int getMaxUserLength() { return maxUserLen; } /** * Get the nickPrefixes. * * @return the nick prefixes */ public List<String> getNickPrefixes() { return new ArrayList<String>(nickPrefixMap.values()); } /** * Get The nickprefixes supported * * @return the nick prefixes */ public Map<String, String> getNickPrefixMap() { return nickPrefixMap; } /** * The status prefixes supported * * @return the supported status prefixes */ public String[] getStatusPrefixes() { return statusPrefixes; } /** * Get the channel prefixes * * @return the channel prefixes supported on the server */ public String[] getChannelPrefixes() { return channelPrefixes; } }