2011-06-02 15:24:26 +02:00
|
|
|
package com.earth2me.essentials.xmpp;
|
|
|
|
|
2011-06-07 01:08:31 +02:00
|
|
|
import com.earth2me.essentials.Console;
|
2011-06-02 15:24:26 +02:00
|
|
|
import com.earth2me.essentials.IConf;
|
2021-06-07 14:49:33 +02:00
|
|
|
import com.earth2me.essentials.config.EssentialsConfiguration;
|
2013-06-08 23:31:19 +02:00
|
|
|
import com.earth2me.essentials.utils.FormatUtil;
|
2015-04-15 06:06:16 +02:00
|
|
|
import net.ess3.api.IUser;
|
2011-06-02 15:24:26 +02:00
|
|
|
import org.bukkit.entity.Player;
|
2020-10-03 19:46:05 +02:00
|
|
|
import org.jivesoftware.smack.Chat;
|
|
|
|
import org.jivesoftware.smack.ChatManager;
|
|
|
|
import org.jivesoftware.smack.ChatManagerListener;
|
|
|
|
import org.jivesoftware.smack.ConnectionConfiguration;
|
|
|
|
import org.jivesoftware.smack.MessageListener;
|
2012-11-27 19:51:32 +01:00
|
|
|
import org.jivesoftware.smack.Roster.SubscriptionMode;
|
2020-10-03 19:46:05 +02:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2011-06-02 15:24:26 +02:00
|
|
|
import org.jivesoftware.smack.packet.Message;
|
|
|
|
import org.jivesoftware.smack.packet.Presence;
|
2011-06-03 03:50:40 +02:00
|
|
|
import org.jivesoftware.smack.util.StringUtils;
|
2011-06-02 15:24:26 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
import java.io.File;
|
2020-10-03 19:46:05 +02:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.logging.Handler;
|
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.LogRecord;
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
import java.util.logging.SimpleFormatter;
|
2015-04-15 06:06:16 +02:00
|
|
|
|
2020-05-09 14:21:02 +02:00
|
|
|
import static com.earth2me.essentials.I18n.tl;
|
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
public class XMPPManager extends Handler implements MessageListener, ChatManagerListener, IConf {
|
2022-06-27 20:54:10 +02:00
|
|
|
private static final Logger logger = EssentialsXMPP.getWrappedLogger();
|
2015-04-15 06:06:16 +02:00
|
|
|
private static final SimpleFormatter formatter = new SimpleFormatter();
|
2021-06-07 14:49:33 +02:00
|
|
|
private final transient EssentialsConfiguration config;
|
2019-01-05 19:42:09 +01:00
|
|
|
private final transient Map<String, Chat> chats = Collections.synchronizedMap(new HashMap<>());
|
|
|
|
private final transient Set<LogRecord> logrecords = Collections.synchronizedSet(new HashSet<>());
|
2015-04-15 06:06:16 +02:00
|
|
|
private final transient IEssentialsXMPP parent;
|
2020-10-03 19:46:05 +02:00
|
|
|
private transient XMPPConnection connection;
|
|
|
|
private transient ChatManager chatManager;
|
2015-04-15 06:06:16 +02:00
|
|
|
private transient List<String> logUsers;
|
|
|
|
private transient Level logLevel;
|
|
|
|
private transient boolean ignoreLagMessages = true;
|
|
|
|
private transient Thread loggerThread;
|
|
|
|
private transient boolean threadrunning = true;
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2019-01-05 19:42:09 +01:00
|
|
|
XMPPManager(final IEssentialsXMPP parent) {
|
2015-04-15 06:06:16 +02:00
|
|
|
super();
|
|
|
|
this.parent = parent;
|
2021-06-07 14:49:33 +02:00
|
|
|
config = new EssentialsConfiguration(new File(parent.getDataFolder(), "config.yml"), "/config.yml", EssentialsXMPP.class);
|
2015-04-15 06:06:16 +02:00
|
|
|
reloadConfig();
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2019-01-05 19:42:09 +01:00
|
|
|
boolean sendMessage(final String address, final String message) {
|
2015-04-15 06:06:16 +02:00
|
|
|
if (address != null && !address.isEmpty()) {
|
|
|
|
try {
|
|
|
|
startChat(address);
|
|
|
|
final Chat chat;
|
|
|
|
synchronized (chats) {
|
|
|
|
chat = chats.get(address);
|
|
|
|
}
|
|
|
|
if (chat != null) {
|
|
|
|
if (!connection.isConnected()) {
|
|
|
|
disconnect();
|
|
|
|
connect();
|
|
|
|
}
|
|
|
|
chat.sendMessage(FormatUtil.stripFormat(message));
|
|
|
|
return true;
|
|
|
|
}
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final XMPPException ex) {
|
2015-04-15 06:06:16 +02:00
|
|
|
disableChat(address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
@Override
|
|
|
|
public void processMessage(final Chat chat, final Message msg) {
|
|
|
|
// Normally we should log the error message
|
|
|
|
// But we would create a loop if the connection to a log-user fails.
|
|
|
|
if (msg.getType() != Message.Type.error && msg.getBody().length() > 0) {
|
|
|
|
final String message = msg.getBody();
|
|
|
|
switch (message.charAt(0)) {
|
|
|
|
case '@':
|
|
|
|
sendPrivateMessage(chat, message);
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
sendCommand(chat, message);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
final IUser sender = parent.getUserByAddress(StringUtils.parseBareAddress(chat.getParticipant()));
|
|
|
|
parent.broadcastMessage(sender, "=" + sender.getBase().getDisplayName() + ": " + message, StringUtils.parseBareAddress(chat.getParticipant()));
|
2020-10-03 19:46:05 +02:00
|
|
|
break;
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
private boolean connect() {
|
2021-06-07 14:49:33 +02:00
|
|
|
final String server = config.getString("xmpp.server", null);
|
2015-04-15 06:06:16 +02:00
|
|
|
if (server == null || server.equals("example.com")) {
|
2020-05-09 14:21:02 +02:00
|
|
|
logger.log(Level.WARNING, tl("xmppNotConfigured"));
|
2015-04-15 06:06:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
final int port = config.getInt("xmpp.port", 5222);
|
|
|
|
final String serviceName = config.getString("xmpp.servicename", server);
|
2021-06-07 14:49:33 +02:00
|
|
|
final String xmppuser = config.getString("xmpp.user", null);
|
|
|
|
final String password = config.getString("xmpp.password", null);
|
2021-02-21 16:46:32 +01:00
|
|
|
final boolean requireTLS = config.getBoolean("xmpp.require-server-tls", false);
|
2015-04-15 06:06:16 +02:00
|
|
|
final ConnectionConfiguration connConf = new ConnectionConfiguration(server, port, serviceName);
|
2020-10-03 19:46:05 +02:00
|
|
|
final String stringBuilder = "Connecting to xmpp server " + server + ":" + port + " as user " + xmppuser + ".";
|
2019-01-05 19:42:09 +01:00
|
|
|
logger.log(Level.INFO, stringBuilder);
|
2015-04-15 06:06:16 +02:00
|
|
|
connConf.setSASLAuthenticationEnabled(config.getBoolean("xmpp.sasl-enabled", false));
|
|
|
|
connConf.setSendPresence(true);
|
|
|
|
connConf.setReconnectionAllowed(true);
|
|
|
|
connConf.setDebuggerEnabled(config.getBoolean("debug", false));
|
2021-02-21 16:46:32 +01:00
|
|
|
if (requireTLS) {
|
|
|
|
// "enabled" (TLS optional) is the default
|
|
|
|
connConf.setSecurityMode(ConnectionConfiguration.SecurityMode.required);
|
|
|
|
}
|
2015-04-15 06:06:16 +02:00
|
|
|
connection = new XMPPConnection(connConf);
|
|
|
|
try {
|
|
|
|
connection.connect();
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
connection.login(xmppuser, password, "Essentials-XMPP");
|
|
|
|
connection.sendPacket(new Presence(Presence.Type.available, "No one online.", 2, Presence.Mode.available));
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
connection.getRoster().setSubscriptionMode(SubscriptionMode.accept_all);
|
|
|
|
chatManager = connection.getChatManager();
|
|
|
|
chatManager.addChatListener(this);
|
|
|
|
return true;
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final XMPPException ex) {
|
2019-01-05 19:42:09 +01:00
|
|
|
logger.log(Level.WARNING, "Failed to connect to server: " + server, ex);
|
2021-02-21 16:46:32 +01:00
|
|
|
logger.log(Level.WARNING, "Connected: " + connection.isConnected());
|
|
|
|
logger.log(Level.WARNING, "Secure: " + connection.isSecureConnection());
|
|
|
|
logger.log(Level.WARNING, "Using TLS: " + connection.isUsingTLS());
|
|
|
|
logger.log(Level.WARNING, "Authenticated: " + connection.getSASLAuthentication().isAuthenticated());
|
2015-04-15 06:06:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2019-01-05 19:42:09 +01:00
|
|
|
final void disconnect() {
|
2015-04-15 06:06:16 +02:00
|
|
|
if (loggerThread != null) {
|
|
|
|
loggerThread.interrupt();
|
|
|
|
}
|
|
|
|
if (chatManager != null) {
|
|
|
|
chatManager.removeChatListener(this);
|
|
|
|
chatManager = null;
|
|
|
|
}
|
|
|
|
if (connection != null) {
|
|
|
|
connection.disconnect(new Presence(Presence.Type.unavailable));
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2019-01-05 19:42:09 +01:00
|
|
|
final void updatePresence() {
|
2020-05-09 14:21:02 +02:00
|
|
|
if (connection == null) {
|
|
|
|
parent.getEss().getLogger().warning(tl("xmppNotConfigured"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
final int usercount;
|
|
|
|
final StringBuilder stringBuilder = new StringBuilder();
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
usercount = parent.getEss().getOnlinePlayers().size();
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
if (usercount == 0) {
|
|
|
|
final String presenceMsg = "No one online.";
|
|
|
|
connection.sendPacket(new Presence(Presence.Type.available, presenceMsg, 2, Presence.Mode.dnd));
|
|
|
|
}
|
|
|
|
if (usercount == 1) {
|
|
|
|
final String presenceMsg = "1 player online.";
|
|
|
|
connection.sendPacket(new Presence(Presence.Type.available, presenceMsg, 2, Presence.Mode.available));
|
|
|
|
}
|
|
|
|
if (usercount > 1) {
|
|
|
|
stringBuilder.append(usercount).append(" players online.");
|
|
|
|
connection.sendPacket(new Presence(Presence.Type.available, stringBuilder.toString(), 2, Presence.Mode.available));
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
@Override
|
|
|
|
public void chatCreated(final Chat chat, final boolean createdLocally) {
|
|
|
|
if (!createdLocally) {
|
|
|
|
chat.addMessageListener(this);
|
|
|
|
final Chat old = chats.put(StringUtils.parseBareAddress(chat.getParticipant()), chat);
|
|
|
|
if (old != null) {
|
|
|
|
old.removeMessageListener(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
@Override
|
|
|
|
public final void reloadConfig() {
|
2019-01-05 19:42:09 +01:00
|
|
|
logger.removeHandler(this);
|
2015-04-15 06:06:16 +02:00
|
|
|
config.load();
|
|
|
|
synchronized (chats) {
|
|
|
|
disconnect();
|
|
|
|
chats.clear();
|
|
|
|
if (!connect()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
startLoggerThread();
|
|
|
|
}
|
|
|
|
if (config.getBoolean("log-enabled", false)) {
|
2019-01-05 19:42:09 +01:00
|
|
|
logger.addHandler(this);
|
2021-06-07 14:49:33 +02:00
|
|
|
logUsers = config.getList("log-users", String.class);
|
2015-04-15 06:06:16 +02:00
|
|
|
final String level = config.getString("log-level", "info");
|
|
|
|
try {
|
|
|
|
logLevel = Level.parse(level.toUpperCase(Locale.ENGLISH));
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final IllegalArgumentException e) {
|
2015-04-15 06:06:16 +02:00
|
|
|
logLevel = Level.INFO;
|
|
|
|
}
|
|
|
|
ignoreLagMessages = config.getBoolean("ignore-lag-messages", true);
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
@Override
|
|
|
|
public void publish(final LogRecord logRecord) {
|
|
|
|
try {
|
|
|
|
if (ignoreLagMessages && logRecord.getMessage().equals("Can't keep up! Did the system time change, or is the server overloaded?")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (logRecord.getLevel().intValue() >= logLevel.intValue()) {
|
|
|
|
synchronized (logrecords) {
|
|
|
|
logrecords.add(logRecord);
|
|
|
|
}
|
|
|
|
}
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final Exception ignored) {
|
2015-04-15 06:06:16 +02:00
|
|
|
// Ignore all exceptions
|
|
|
|
// Otherwise we create a loop.
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
@Override
|
|
|
|
public void flush() {
|
|
|
|
// Ignore this
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
@Override
|
|
|
|
public void close() throws SecurityException {
|
|
|
|
// Ignore this
|
|
|
|
}
|
2011-07-07 18:54:25 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
private void startLoggerThread() {
|
2019-01-05 19:42:09 +01:00
|
|
|
loggerThread = new Thread(() -> {
|
|
|
|
final Set<LogRecord> copy = new HashSet<>();
|
|
|
|
final Set<String> failedUsers = new HashSet<>();
|
|
|
|
while (threadrunning) {
|
|
|
|
synchronized (logrecords) {
|
|
|
|
if (!logrecords.isEmpty()) {
|
|
|
|
copy.addAll(logrecords);
|
|
|
|
logrecords.clear();
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
2019-01-05 19:42:09 +01:00
|
|
|
}
|
|
|
|
if (!copy.isEmpty()) {
|
2020-10-03 19:46:05 +02:00
|
|
|
for (final String user : logUsers) {
|
2019-01-05 19:42:09 +01:00
|
|
|
try {
|
|
|
|
XMPPManager.this.startChat(user);
|
2020-10-03 19:46:05 +02:00
|
|
|
for (final LogRecord logRecord : copy) {
|
2019-01-05 19:42:09 +01:00
|
|
|
final String message = formatter.format(logRecord);
|
|
|
|
if (!XMPPManager.this.sendMessage(user, FormatUtil.stripLogColorFormat(message))) {
|
|
|
|
failedUsers.add(user);
|
|
|
|
break;
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
2019-01-05 19:42:09 +01:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final XMPPException ex) {
|
2019-01-05 19:42:09 +01:00
|
|
|
failedUsers.add(user);
|
|
|
|
logger.removeHandler(XMPPManager.this);
|
|
|
|
logger.log(Level.SEVERE, "Failed to deliver log message! Disabling logging to XMPP.", ex);
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-05 19:42:09 +01:00
|
|
|
logUsers.removeAll(failedUsers);
|
|
|
|
if (logUsers.isEmpty()) {
|
|
|
|
logger.removeHandler(XMPPManager.this);
|
2015-04-15 06:06:16 +02:00
|
|
|
threadrunning = false;
|
|
|
|
}
|
2019-01-05 19:42:09 +01:00
|
|
|
copy.clear();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
Thread.sleep(2000);
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final InterruptedException ex) {
|
2019-01-05 19:42:09 +01:00
|
|
|
threadrunning = false;
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-05 19:42:09 +01:00
|
|
|
logger.removeHandler(XMPPManager.this);
|
2015-04-15 06:06:16 +02:00
|
|
|
});
|
|
|
|
loggerThread.start();
|
|
|
|
}
|
2013-10-13 21:57:41 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
private void startChat(final String address) throws XMPPException {
|
|
|
|
if (chatManager == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
synchronized (chats) {
|
|
|
|
if (!chats.containsKey(address)) {
|
|
|
|
final Chat chat = chatManager.createChat(address, this);
|
|
|
|
if (chat == null) {
|
|
|
|
throw new XMPPException("Could not start Chat with " + address);
|
|
|
|
}
|
|
|
|
chats.put(address, chat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-13 21:57:41 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
private void sendPrivateMessage(final Chat chat, final String message) {
|
|
|
|
final String[] parts = message.split(" ", 2);
|
|
|
|
if (parts.length == 2) {
|
|
|
|
final List<Player> matches = parent.getServer().matchPlayer(parts[0].substring(1));
|
2013-10-13 21:57:41 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
if (matches.isEmpty()) {
|
|
|
|
try {
|
|
|
|
chat.sendMessage("User " + parts[0] + " not found");
|
2020-10-03 19:46:05 +02:00
|
|
|
} catch (final XMPPException ex) {
|
2019-01-05 19:42:09 +01:00
|
|
|
logger.log(Level.WARNING, "Failed to send xmpp message.", ex);
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
final String from = "[" + parent.getUserByAddress(StringUtils.parseBareAddress(chat.getParticipant())) + ">";
|
2020-10-03 19:46:05 +02:00
|
|
|
for (final Player p : matches) {
|
2015-04-15 06:06:16 +02:00
|
|
|
p.sendMessage(from + p.getDisplayName() + "] " + message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-13 21:57:41 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
private void sendCommand(final Chat chat, final String message) {
|
2021-06-07 14:49:33 +02:00
|
|
|
if (config.getList("op-users", String.class).contains(StringUtils.parseBareAddress(chat.getParticipant()))) {
|
2020-12-11 17:36:29 +01:00
|
|
|
parent.getServer().getScheduler().runTask(parent, () -> {
|
|
|
|
try {
|
|
|
|
parent.getServer().dispatchCommand(Console.getInstance().getCommandSender(), message.substring(1));
|
|
|
|
} catch (final Exception ex) {
|
|
|
|
logger.log(Level.SEVERE, ex.getMessage(), ex);
|
|
|
|
}
|
|
|
|
});
|
2015-04-15 06:06:16 +02:00
|
|
|
}
|
|
|
|
}
|
2013-10-13 21:57:41 +02:00
|
|
|
|
2015-04-15 06:06:16 +02:00
|
|
|
private void disableChat(final String address) {
|
|
|
|
final Chat chat = chats.get(address);
|
|
|
|
if (chat != null) {
|
|
|
|
chat.removeMessageListener(this);
|
|
|
|
chats.remove(address);
|
|
|
|
}
|
|
|
|
}
|
2019-01-05 18:48:44 +01:00
|
|
|
|
|
|
|
public boolean isConfigValid() {
|
2021-06-07 14:49:33 +02:00
|
|
|
final String server = config.getString("xmpp.server", null);
|
2019-01-09 14:57:18 +01:00
|
|
|
return server != null && !server.equals("example.com");
|
2019-01-05 18:48:44 +01:00
|
|
|
}
|
2011-06-02 15:24:26 +02:00
|
|
|
}
|