PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/EssentialsXMPP/src/com/earth2me/essentials/xmpp/XMPPManager.java

https://github.com/YellowFellow/Essentials
Java | 388 lines | 361 code | 21 blank | 6 comment | 46 complexity | eb9aefd0ce85858c240a113ed79df5a0 MD5 | raw file
  1. package com.earth2me.essentials.xmpp;
  2. import com.earth2me.essentials.Console;
  3. import com.earth2me.essentials.EssentialsConf;
  4. import com.earth2me.essentials.IConf;
  5. import com.earth2me.essentials.IUser;
  6. import java.io.File;
  7. import java.util.ArrayList;
  8. import java.util.Collections;
  9. import java.util.HashMap;
  10. import java.util.HashSet;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.Set;
  14. import java.util.logging.Handler;
  15. import java.util.logging.Level;
  16. import java.util.logging.LogRecord;
  17. import java.util.logging.Logger;
  18. import org.bukkit.entity.Player;
  19. import org.bukkit.plugin.java.JavaPlugin;
  20. import org.jivesoftware.smack.Chat;
  21. import org.jivesoftware.smack.ChatManager;
  22. import org.jivesoftware.smack.ChatManagerListener;
  23. import org.jivesoftware.smack.ConnectionConfiguration;
  24. import org.jivesoftware.smack.MessageListener;
  25. import org.jivesoftware.smack.Roster.SubscriptionMode;
  26. import org.jivesoftware.smack.XMPPConnection;
  27. import org.jivesoftware.smack.XMPPException;
  28. import org.jivesoftware.smack.packet.Message;
  29. import org.jivesoftware.smack.packet.Presence;
  30. import org.jivesoftware.smack.util.StringUtils;
  31. public class XMPPManager extends Handler implements MessageListener, ChatManagerListener, IConf
  32. {
  33. private static final Logger LOGGER = Logger.getLogger("Minecraft");
  34. private final transient EssentialsConf config;
  35. private transient XMPPConnection connection;
  36. private transient ChatManager chatManager;
  37. private final transient Map<String, Chat> chats = Collections.synchronizedMap(new HashMap<String, Chat>());
  38. private final transient Set<LogRecord> logrecords = Collections.synchronizedSet(new HashSet<LogRecord>());
  39. private final transient IEssentialsXMPP parent;
  40. private transient List<String> logUsers;
  41. private transient Level logLevel;
  42. private transient boolean ignoreLagMessages = true;
  43. private transient Thread loggerThread;
  44. private transient boolean threadrunning = true;
  45. public XMPPManager(final IEssentialsXMPP parent)
  46. {
  47. super();
  48. this.parent = parent;
  49. config = new EssentialsConf(new File(parent.getDataFolder(), "config.yml"));
  50. config.setTemplateName("/config.yml", EssentialsXMPP.class);
  51. reloadConfig();
  52. }
  53. public boolean sendMessage(final String address, final String message)
  54. {
  55. if (address != null && !address.isEmpty())
  56. {
  57. try
  58. {
  59. startChat(address);
  60. final Chat chat;
  61. synchronized (chats)
  62. {
  63. chat = chats.get(address);
  64. }
  65. if (chat != null)
  66. {
  67. if (!connection.isConnected()) {
  68. disconnect();
  69. connect();
  70. }
  71. chat.sendMessage(message.replaceAll("ยง[0-9a-f]", ""));
  72. return true;
  73. }
  74. }
  75. catch (XMPPException ex)
  76. {
  77. disableChat(address);
  78. }
  79. }
  80. return false;
  81. }
  82. @Override
  83. public void processMessage(final Chat chat, final Message msg)
  84. {
  85. // Normally we should log the error message
  86. // But we would create a loop if the connection to a log-user fails.
  87. if (msg.getType() != Message.Type.error && msg.getBody().length() > 0)
  88. {
  89. final String message = msg.getBody();
  90. switch (message.charAt(0))
  91. {
  92. case '@':
  93. sendPrivateMessage(chat, message);
  94. break;
  95. case '/':
  96. sendCommand(chat, message);
  97. break;
  98. default:
  99. final IUser sender = parent.getUserByAddress(StringUtils.parseBareAddress(chat.getParticipant()));
  100. parent.broadcastMessage(sender, "="+sender.getDisplayName()+": "+ message);
  101. }
  102. }
  103. }
  104. private boolean connect()
  105. {
  106. final String server = config.getString("xmpp.server");
  107. if (server == null || server.equals("example.com"))
  108. {
  109. LOGGER.log(Level.WARNING, "config broken for xmpp");
  110. return false;
  111. }
  112. final int port = config.getInt("xmpp.port", 5222);
  113. final String serviceName = config.getString("xmpp.servicename", server);
  114. final String xmppuser = config.getString("xmpp.user");
  115. final String password = config.getString("xmpp.password");
  116. final ConnectionConfiguration connConf = new ConnectionConfiguration(server, port, serviceName);
  117. final StringBuilder stringBuilder = new StringBuilder();
  118. stringBuilder.append("Connecting to xmpp server ").append(server).append(":").append(port);
  119. stringBuilder.append(" as user ").append(xmppuser).append(".");
  120. LOGGER.log(Level.INFO, stringBuilder.toString());
  121. connConf.setSASLAuthenticationEnabled(config.getBoolean("xmpp.sasl-enabled", false));
  122. connConf.setSendPresence(true);
  123. connConf.setReconnectionAllowed(true);
  124. connection = new XMPPConnection(connConf);
  125. try
  126. {
  127. connection.connect();
  128. connection.login(xmppuser, password);
  129. connection.getRoster().setSubscriptionMode(SubscriptionMode.accept_all);
  130. chatManager = connection.getChatManager();
  131. chatManager.addChatListener(this);
  132. return true;
  133. }
  134. catch (XMPPException ex)
  135. {
  136. LOGGER.log(Level.WARNING, "Failed to connect to server: " + server, ex);
  137. return false;
  138. }
  139. }
  140. public final void disconnect()
  141. {
  142. if (loggerThread != null)
  143. {
  144. loggerThread.interrupt();
  145. }
  146. if (chatManager != null)
  147. {
  148. chatManager.removeChatListener(this);
  149. chatManager = null;
  150. }
  151. if (connection != null)
  152. {
  153. connection.disconnect(new Presence(Presence.Type.unavailable));
  154. }
  155. }
  156. @Override
  157. public void chatCreated(final Chat chat, final boolean createdLocally)
  158. {
  159. if (!createdLocally)
  160. {
  161. chat.addMessageListener(this);
  162. final Chat old = chats.put(StringUtils.parseBareAddress(chat.getParticipant()), chat);
  163. if (old != null)
  164. {
  165. old.removeMessageListener(this);
  166. }
  167. }
  168. }
  169. @Override
  170. public final void reloadConfig()
  171. {
  172. LOGGER.removeHandler(this);
  173. config.load();
  174. synchronized (chats)
  175. {
  176. disconnect();
  177. chats.clear();
  178. if (!connect())
  179. {
  180. return;
  181. }
  182. startLoggerThread();
  183. }
  184. if (config.getBoolean("log-enabled", false))
  185. {
  186. LOGGER.addHandler(this);
  187. logUsers = config.getStringList("log-users", new ArrayList<String>());
  188. final String level = config.getString("log-level", "info");
  189. try
  190. {
  191. logLevel = Level.parse(level.toUpperCase());
  192. }
  193. catch (IllegalArgumentException e)
  194. {
  195. logLevel = Level.INFO;
  196. }
  197. ignoreLagMessages = config.getBoolean("ignore-lag-messages", true);
  198. }
  199. }
  200. @Override
  201. public void publish(final LogRecord logRecord)
  202. {
  203. try
  204. {
  205. if (ignoreLagMessages && logRecord.getMessage().equals("Can't keep up! Did the system time change, or is the server overloaded?"))
  206. {
  207. return;
  208. }
  209. if (logRecord.getLevel().intValue() >= logLevel.intValue())
  210. {
  211. synchronized (logrecords)
  212. {
  213. logrecords.add(logRecord);
  214. }
  215. }
  216. }
  217. catch (Exception e)
  218. {
  219. // Ignore all exceptions
  220. // Otherwise we create a loop.
  221. }
  222. }
  223. @Override
  224. public void flush()
  225. {
  226. // Ignore this
  227. }
  228. @Override
  229. public void close() throws SecurityException
  230. {
  231. // Ignore this
  232. }
  233. private void startLoggerThread()
  234. {
  235. loggerThread = new Thread(new Runnable()
  236. {
  237. @Override
  238. public void run()
  239. {
  240. final Set<LogRecord> copy = new HashSet<LogRecord>();
  241. final Set<String> failedUsers = new HashSet<String>();
  242. while (threadrunning)
  243. {
  244. synchronized (logrecords)
  245. {
  246. if (!logrecords.isEmpty())
  247. {
  248. copy.addAll(logrecords);
  249. logrecords.clear();
  250. }
  251. }
  252. if (!copy.isEmpty())
  253. {
  254. for (String user : logUsers)
  255. {
  256. try
  257. {
  258. XMPPManager.this.startChat(user);
  259. for (LogRecord logRecord : copy)
  260. {
  261. final String message = String.format("[" + logRecord.getLevel().getLocalizedName() + "] " + logRecord.getMessage(), logRecord.getParameters());
  262. if (!XMPPManager.this.sendMessage(user, message))
  263. {
  264. failedUsers.add(user);
  265. break;
  266. }
  267. }
  268. }
  269. catch (XMPPException ex)
  270. {
  271. failedUsers.add(user);
  272. LOGGER.removeHandler(XMPPManager.this);
  273. LOGGER.log(Level.SEVERE, "Failed to deliver log message! Disabling logging to XMPP.", ex);
  274. }
  275. }
  276. logUsers.removeAll(failedUsers);
  277. if (logUsers.isEmpty())
  278. {
  279. LOGGER.removeHandler(XMPPManager.this);
  280. threadrunning = false;
  281. }
  282. copy.clear();
  283. }
  284. try
  285. {
  286. Thread.sleep(2000);
  287. }
  288. catch (InterruptedException ex)
  289. {
  290. threadrunning = false;
  291. }
  292. }
  293. LOGGER.removeHandler(XMPPManager.this);
  294. }
  295. });
  296. loggerThread.start();
  297. }
  298. private void startChat(final String address) throws XMPPException
  299. {
  300. if (chatManager == null)
  301. {
  302. return;
  303. }
  304. synchronized (chats)
  305. {
  306. if (!chats.containsKey(address))
  307. {
  308. final Chat chat = chatManager.createChat(address, this);
  309. if (chat == null)
  310. {
  311. throw new XMPPException("Could not start Chat with " + address);
  312. }
  313. chats.put(address, chat);
  314. }
  315. }
  316. }
  317. private void sendPrivateMessage(final Chat chat, final String message)
  318. {
  319. final String[] parts = message.split(" ", 2);
  320. if (parts.length == 2)
  321. {
  322. final List<Player> matches = parent.getServer().matchPlayer(parts[0].substring(1));
  323. if (matches.isEmpty())
  324. {
  325. try
  326. {
  327. chat.sendMessage("User " + parts[0] + " not found");
  328. }
  329. catch (XMPPException ex)
  330. {
  331. LOGGER.log(Level.WARNING, "Failed to send xmpp message.", ex);
  332. }
  333. }
  334. else
  335. {
  336. final String from = "[" + parent.getUserByAddress(StringUtils.parseBareAddress(chat.getParticipant())) + ">";
  337. for (Player p : matches)
  338. {
  339. p.sendMessage(from + p.getDisplayName() + "] " + message);
  340. }
  341. }
  342. }
  343. }
  344. private void sendCommand(final Chat chat, final String message)
  345. {
  346. if (config.getStringList("op-users", new ArrayList<String>()).contains(StringUtils.parseBareAddress(chat.getParticipant())))
  347. {
  348. try
  349. {
  350. parent.getServer().dispatchCommand(Console.getCommandSender(parent.getServer()), message.substring(1));
  351. }
  352. catch (Exception ex)
  353. {
  354. LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
  355. }
  356. }
  357. }
  358. private void disableChat(final String address)
  359. {
  360. final Chat chat = chats.get(address);
  361. if (chat != null)
  362. {
  363. chat.removeMessageListener(this);
  364. chats.remove(address);
  365. }
  366. }
  367. }