/**
* Copyright (C) 2010-2014 Leon Blakey <lord.quackstar at gmail.com>
*
* This file is part of PircBotX.
*
* PircBotX is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* PircBotX is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* PircBotX. If not, see <http://www.gnu.org/licenses/>.
*/
package org.pircbotx;
import com.google.common.collect.ImmutableMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.StringTokenizer;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a giant info bean of various things about the server. This is
* separate from the {@link PircBotX} class due to its length
* <p/>
* Most info thanks to <a href="www.irc.org/tech_docs/005.html">this great
* website
* </a> on what each one does
* <p>
* Note: On get methods that return int, -1 means the value exists but is
* unparsable natively
*
* @author Leon Blakey
*/
@Data
@Setter(AccessLevel.NONE)
public class ServerInfo {
private static final Logger log = LoggerFactory.getLogger(ServerInfo.class);
protected final PircBotX bot;
//004 information
protected String serverName;
protected String serverVersion;
protected String userModes;
//005 information
protected LinkedHashMap<String, String> isupportRaw = new LinkedHashMap<String, String>();
protected String prefixes;
protected String channelTypes;
protected String channelModes;
protected int maxModes;
protected int maxChannels;
protected String chanlimit;
protected int maxNickLength;
protected int maxBans;
protected ImmutableMap<String, Integer> maxList;
protected String network;
protected String exceptBans;
protected String exceptInvites;
protected String invites;
protected boolean wallOps;
protected boolean wallVoices;
protected String statusMessage;
protected String caseMapping;
protected String eList;
protected int topicLength;
protected int kickLength;
protected int channelLength;
protected String channelIDLength;
protected String standard;
protected int silence;
protected boolean RFC2812;
protected boolean penalty;
protected boolean forcedNickChanges;
protected boolean safeList;
protected int awayLength;
protected boolean noQuit;
protected boolean userIPExists;
protected boolean cPrivMsgExists;
protected boolean cNoticeExists;
protected int maxTargets;
protected boolean knockExists;
protected boolean vChannels;
protected int watchMax;
protected boolean whoX;
protected boolean callerID;
protected boolean accept;
protected String language;
protected String extBanPrefix;
protected String extBanList;
//Other information
@Setter(AccessLevel.PROTECTED)
protected String motd;
protected int highestConnections;
protected int highestClients;
protected int totalUsers;
protected int totalInvisibleUsers;
protected int totalServers;
protected int totalOperatorsOnline;
protected int totalUnknownConnections;
protected int totalChannelsFormed;
protected int serverUsers;
protected int connectedServers;
public void parse(int code, List<String> parsedLine) {
//Pass off to speicific methods
if (code == 004)
parse004(parsedLine);
else if (code == 005)
parse005(parsedLine);
}
protected void parse004(List<String> parsedLine) {
//004 PircBotX pratchett.freenode.net ircd-seven-1.1.3 DOQRSZaghilopswz CFILMPQbcefgijklmnopqrstvz bkloveqjfI
serverName = Utils.tryGetIndex(parsedLine, 1, null);
serverVersion = Utils.tryGetIndex(parsedLine, 2, null);
userModes = Utils.tryGetIndex(parsedLine, 3, null);
channelModes = Utils.tryGetIndex(parsedLine, 4, null);
}
protected void parse005(List<String> parsedLine) {
//REFERENCE: http://www.irc.org/tech_docs/005.html
for (String curItem : parsedLine) {
String[] itemParts = curItem.split("=", 2);
String key = itemParts[0];
String value = (itemParts.length == 2) ? itemParts[1] : "";
isupportRaw.put(key, value);
if (key.equalsIgnoreCase("PREFIX"))
prefixes = value;
else if (key.equalsIgnoreCase("CHANTYPES"))
channelTypes = value;
else if (key.equalsIgnoreCase("CHANMODES"))
channelModes = value;
else if (key.equalsIgnoreCase("MODES"))
maxModes = tryParseInt("MODES", value);
else if (key.equalsIgnoreCase("MAXCHANNELS"))
maxChannels = tryParseInt("MAXCHANNELS", value);
else if (key.equalsIgnoreCase("CHANLIMIT"))
chanlimit = value;
else if (key.equalsIgnoreCase("NICKLEN"))
maxNickLength = tryParseInt("NICKLEN", value);
else if (key.equalsIgnoreCase("MAXBANS"))
maxBans = tryParseInt("MAXBANS", value);
else if (key.equalsIgnoreCase("MAXLIST")) {
StringTokenizer maxListTokens = new StringTokenizer(value, ":,");
ImmutableMap.Builder<String, Integer> maxListBuilder = ImmutableMap.builder();
while (maxListTokens.hasMoreTokens()) {
String next = maxListTokens.nextToken();
maxListBuilder.put(next, tryParseInt("MAXLIST>" + next, maxListTokens.nextToken()));
}
maxList = maxListBuilder.build();
} else if (key.equalsIgnoreCase("NETWORK"))
network = value;
else if (key.equalsIgnoreCase("EXCEPTS"))
exceptBans = value;
else if (key.equalsIgnoreCase("INVEX"))
exceptInvites = value;
else if (key.equalsIgnoreCase("WALLCHOPS"))
wallOps = true;
else if (key.equalsIgnoreCase("WALLVOICES"))
wallVoices = true;
else if (key.equalsIgnoreCase("STATUSMSG"))
statusMessage = value;
else if (key.equalsIgnoreCase("CASEMAPPING"))
caseMapping = value;
else if (key.equalsIgnoreCase("ELIST"))
eList = value;
else if (key.equalsIgnoreCase("TOPICLEN"))
topicLength = tryParseInt("TOPICLEN", value);
else if (key.equalsIgnoreCase("KICKLEN"))
kickLength = tryParseInt("KICKLEN", value);
else if (key.equalsIgnoreCase("CHANNELLEN"))
channelLength = tryParseInt("CHANNELLEN", value);
else if (key.equalsIgnoreCase("CHIDLEN"))
channelIDLength = "!:" + tryParseInt("CHIDLEN", value);
else if (key.equalsIgnoreCase("IDCHAN"))
channelIDLength = value;
else if (key.equalsIgnoreCase("STD"))
standard = value;
else if (key.equalsIgnoreCase("SILENCE"))
silence = tryParseInt("SILENCE", value);
else if (key.equalsIgnoreCase("RFC2812"))
RFC2812 = true;
else if (key.equalsIgnoreCase("PENALTY"))
penalty = true;
else if (key.equalsIgnoreCase("CPRIVMSG"))
cPrivMsgExists = true;
else if (key.equalsIgnoreCase("CNOTICE"))
cNoticeExists = true;
else if (key.equalsIgnoreCase("SAFELIST"))
safeList = true;
else if (key.equalsIgnoreCase("KNOCK"))
knockExists = true;
else if (key.equalsIgnoreCase("WHOX"))
whoX = true;
else if (key.equalsIgnoreCase("CALLERID") || key.equalsIgnoreCase("ACCEPT"))
callerID = true;
else if (key.equalsIgnoreCase("USERIP"))
userIPExists = true;
else if (key.equalsIgnoreCase("CNOTICE"))
cNoticeExists = true;
else if (key.equalsIgnoreCase("EXTBAN")) {
if (value.contains(",")) {
String[] valueSplit = StringUtils.split(value, ",", 2);
if (valueSplit.length == 2) {
extBanPrefix = valueSplit[0];
extBanList = valueSplit[1];
} else {
extBanPrefix = null;
extBanList = valueSplit[0];
}
} else {
extBanList = value;
}
}
}
//Freenode
//005 PircBotX CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQcgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server
//005 PircBotX CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server
//005 PircBotX EXTBAN=$,arx WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server
//Rizon
//005 PircBotX CALLERID CASEMAPPING=rfc1459 DEAF=D KICKLEN=160 MODES=4 NICKLEN=30 TOPICLEN=390 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ NETWORK=Rizon MAXLIST=beI:100 TARGMAX=ACCEPT:,KICK:1,LIST:1,NAMES:1,NOTICE:4,PRIVMSG:4,WHOIS:1 CHANTYPES=# :are supported by this server
//005 PircBotX CHANLIMIT=#:75 CHANNELLEN=50 CHANMODES=beI,k,l,BCMNORScimnpstz AWAYLEN=160 ELIST=CMNTU SAFELIST KNOCK NAMESX UHNAMES FNC EXCEPTS=e INVEX=I :are supported by this server
//Mozilla
//005 QTest AWAYLEN=200 CASEMAPPING=rfc1459 CHANMODES=Zbeg,k,FLfjl,ABCDKMNOQRSTcimnprstuz CHANNELLEN=64 CHANTYPES=# CHARSET=ascii ELIST=MU ESILENCE EXCEPTS=e EXTBAN=,ABCNOQRSTUcmprz FNC KICKLEN=255 MAP :are supported by this server
//005 QTest MAXBANS=60 MAXCHANNELS=100 MAXPARA=32 MAXTARGETS=20 MODES=20 NAMESX NETWORK=Mozilla NICKLEN=31 OPERLOG OVERRIDE PREFIX=(Yqaohv)!~&@%+ SECURELIST SILENCE=32 :are supported by this server
//005 QTest SSL=[::]:6697 STARTTLS STATUSMSG=!~&@%+ TOPICLEN=307 UHNAMES USERIP VBANLIST WALLCHOPS WALLVOICES WATCH=32 :are supported by this server
}
private static int tryParseInt(String name, String value) {
try {
return Integer.parseInt(value);
} catch(NumberFormatException e) {
log.warn("Unparsable server info key '{}' value '{}' {}", name, value, ExceptionUtils.getMessage(e));
return -1;
}
}
/**
* Get all supported server options as a map. Be careful about calling this
* very early in the connection phase as we might not of received all the
* 005 lines yet
*
* @return An <i>immutable copy</i> of the current supported options
*/
public ImmutableMap<String, String> getIsupportRaw() {
return ImmutableMap.copyOf(isupportRaw);
}
public String getISupportValue(String key) {
return isupportRaw.get(key);
}
@SuppressWarnings("unchecked")
public <T extends PircBotX> T getBot() {
return (T) bot;
}
}