Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

RelayChannelHandler.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * Copyright (c) 2006-2013 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.relaybot;
  23. import com.dmdirc.Channel;
  24. import com.dmdirc.ChannelEventHandler;
  25. import com.dmdirc.config.IdentityManager;
  26. import com.dmdirc.parser.common.CallbackManager;
  27. import com.dmdirc.parser.interfaces.ChannelClientInfo;
  28. import com.dmdirc.parser.interfaces.ChannelInfo;
  29. import com.dmdirc.parser.interfaces.Parser;
  30. import com.dmdirc.parser.interfaces.callbacks.ChannelMessageListener;
  31. import com.dmdirc.parser.irc.IRCChannelClientInfo;
  32. import com.dmdirc.parser.irc.IRCParser;
  33. import com.dmdirc.plugins.PluginInfo;
  34. import com.dmdirc.ui.messages.Styliser;
  35. import java.lang.reflect.Field;
  36. import java.lang.reflect.Method;
  37. import java.util.Date;
  38. import java.util.HashMap;
  39. import java.util.Map;
  40. /**
  41. * This class replaces the ChannelHandler in the core to allow intercepting
  42. * callbacks from the parser.
  43. *
  44. * This allows us to hide them from the core and send faked onullnes instead.
  45. *
  46. * @author shane
  47. */
  48. public class RelayChannelHandler implements ChannelMessageListener {
  49. /** My Channel. */
  50. private final Channel myChannel;
  51. /** Core channel handler. */
  52. private final ChannelEventHandler coreChannelHandler;
  53. /** Plugin that owns this RelayChannelHandler. */
  54. private final RelayBotPlugin myPlugin;
  55. /** Known ChannelClients. */
  56. private final Map<String, IRCChannelClientInfo> channelClients
  57. = new HashMap<String, IRCChannelClientInfo>();
  58. /**
  59. * Create a new RelayChannelHandler.
  60. *
  61. * @param myPlugin Parent plugin
  62. * @param myChannel channel to hax!
  63. */
  64. public RelayChannelHandler(final RelayBotPlugin myPlugin,
  65. final Channel myChannel) {
  66. this.myChannel = myChannel;
  67. this.myPlugin = myPlugin;
  68. ChannelEventHandler ceh;
  69. try {
  70. // Get the core Channel handler.
  71. final Field field = myChannel.getClass().getDeclaredField(
  72. "eventHandler");
  73. field.setAccessible(true);
  74. ceh = (ChannelEventHandler) field.get(myChannel);
  75. } catch (IllegalArgumentException ex) {
  76. ceh = null;
  77. } catch (IllegalAccessException ex) {
  78. ceh = null;
  79. } catch (NoSuchFieldException ex) {
  80. ceh = null;
  81. } catch (SecurityException ex) {
  82. ceh = null;
  83. }
  84. coreChannelHandler = ceh;
  85. if (coreChannelHandler != null) {
  86. final CallbackManager cbm = myChannel.getServer().getParser()
  87. .getCallbackManager();
  88. cbm.delCallback(ChannelMessageListener.class, coreChannelHandler);
  89. cbm.addCallback(ChannelMessageListener.class, this,
  90. myChannel.getName());
  91. }
  92. }
  93. /**
  94. * Get a ChannelClient object for the given nick@server.
  95. *
  96. * @param channel Channel
  97. * @param date Date for event
  98. * @param nick Nickname to get channel client for.
  99. * @param sendJoinIfNew Send join if new client seen
  100. * @return Requested ChannelClient Info.
  101. */
  102. private IRCChannelClientInfo getChannelClient(final ChannelInfo channel,
  103. final Date date, final String nick, final boolean sendJoinIfNew) {
  104. final Parser parser = channel.getParser();
  105. final String storeName = parser.getStringConverter().toLowerCase(nick);
  106. synchronized (channelClients) {
  107. if (!channelClients.containsKey(storeName)) {
  108. final RelayClientInfo client = new RelayClientInfo(channel
  109. .getParser(), nick);
  110. final IRCChannelClientInfo newChannelClient
  111. = new IRCChannelClientInfo((IRCParser) channel
  112. .getParser(), client, channel);
  113. colourClient(newChannelClient);
  114. channelClients.put(storeName, newChannelClient);
  115. if (sendJoinIfNew && !client.isServer()) {
  116. coreChannelHandler.onChannelJoin(parser, date, channel,
  117. newChannelClient);
  118. // The nickcolour plugin colours the nicknames on join
  119. // and uses nickname@server when colouring rather than
  120. // the setting the user wanted, we can recolour here to
  121. // fix that.
  122. colourClient(newChannelClient);
  123. }
  124. }
  125. return channelClients.get(storeName);
  126. }
  127. }
  128. /**
  129. * Remove a stored ChannelClient.
  130. *
  131. * @param channel Channel
  132. * @param nick Nickname to get channel client for.
  133. */
  134. private void removeChannelClient(final ChannelInfo channel,
  135. final String nick) {
  136. final Parser parser = channel.getParser();
  137. final String storeName = parser.getStringConverter().toLowerCase(nick);
  138. synchronized (channelClients) {
  139. channelClients.remove(storeName);
  140. }
  141. }
  142. /**
  143. * Rename a stored ChannelClient.
  144. *
  145. * @param channelClient ChannelClient
  146. * @param newNick new Nickname
  147. */
  148. private void renameChannelClient(final IRCChannelClientInfo channelClient,
  149. final String newNick) {
  150. final Parser parser = channelClient.getChannel().getParser();
  151. final String storeName = parser.getStringConverter().toLowerCase(
  152. newNick);
  153. synchronized (channelClients) {
  154. channelClients.remove(channelClient.getClient().toString());
  155. ((RelayClientInfo) channelClient.getClient()).changeNickname(
  156. newNick);
  157. channelClients.put(storeName, channelClient);
  158. }
  159. }
  160. /**
  161. * Restore the callback handling to the coreChannelHandler.
  162. */
  163. public void restoreCoreChannelHandler() {
  164. if (coreChannelHandler != null) {
  165. final CallbackManager cbm = myChannel.getServer().getParser()
  166. .getCallbackManager();
  167. // Force adding this callback to the CBM.
  168. if (cbm instanceof RelayCallbackManager) {
  169. ((RelayCallbackManager) cbm).forceAddCallback(
  170. ChannelMessageListener.class, coreChannelHandler,
  171. myChannel.getName());
  172. } else {
  173. cbm.addCallback(ChannelMessageListener.class,
  174. coreChannelHandler, myChannel.getName());
  175. }
  176. unset();
  177. updateNames();
  178. }
  179. }
  180. /**
  181. * Remove channel message handling from this ChannelHandler.
  182. */
  183. public void unset() {
  184. myChannel.getServer().getParser().getCallbackManager().delCallback(
  185. ChannelMessageListener.class, this);
  186. }
  187. /**
  188. * Colour a client as needed.
  189. *
  190. * @param channelClient Client to colour
  191. */
  192. private void colourClient(final ChannelClientInfo channelClient) {
  193. // Use nick colour plugin to colour the client if available.
  194. final PluginInfo nickColour = myPlugin.getPluginManager()
  195. .getPluginInfoByName("nickcolour");
  196. final boolean fullColour = IdentityManager.getIdentityManager()
  197. .getGlobalConfiguration().getOptionBool(myPlugin.getDomain(), "colourFullName");
  198. final RelayClientInfo client = (RelayClientInfo) channelClient
  199. .getClient();
  200. final boolean oldValue = client.getShowFullNickname();
  201. client.setShowFullNickname(fullColour);
  202. if (nickColour != null && nickColour.isLoaded()) {
  203. try {
  204. final Method gpcl = PluginInfo.class.getDeclaredMethod(
  205. "getPluginClassLoader");
  206. gpcl.setAccessible(true);
  207. final Class<?> nc = nickColour.getPlugin().getClass();
  208. final Method colourClient = nc.getDeclaredMethod("colourClient",
  209. new Class<?>[]{String.class, ChannelClientInfo.class});
  210. colourClient.setAccessible(true);
  211. colourClient.invoke(nickColour.getPlugin(), myChannel
  212. .getServer().getNetwork(), channelClient);
  213. } catch (LinkageError e) {
  214. // If it can't colour then oh well.
  215. } catch (Exception t) {
  216. // If it can't colour then oh well.
  217. }
  218. }
  219. client.setShowFullNickname(oldValue);
  220. }
  221. /**
  222. * Handle the IRCParsers incoming message.
  223. *
  224. * @param parser Parser that sent the message
  225. * @param date Date for event
  226. * @param channel Channel the message went to
  227. * @param channelClient Client who send the message
  228. * @param message Message content
  229. * @param host Host of client
  230. */
  231. @Override
  232. public void onChannelMessage(final Parser parser, final Date date,
  233. final ChannelInfo channel, final ChannelClientInfo channelClient,
  234. final String message, final String host) {
  235. final String channelName = parser.getStringConverter().toLowerCase(
  236. channel.getName());
  237. String botName;
  238. try {
  239. botName = IdentityManager.getIdentityManager().getGlobalConfiguration()
  240. .getOption(myPlugin.getDomain(), channelName);
  241. } catch (IllegalArgumentException iae) {
  242. botName = "";
  243. }
  244. final boolean isBot = parser.getStringConverter().equalsIgnoreCase(
  245. botName, channelClient.getClient().getNickname());
  246. final boolean joinNew = IdentityManager.getIdentityManager()
  247. .getGlobalConfiguration().getOptionBool(myPlugin.getDomain(), "joinOnDiscover");
  248. // See if we need to modify this message
  249. if (channelClient instanceof IRCChannelClientInfo
  250. && !botName.isEmpty() && isBot) {
  251. final String[] bits = message.split(" ", 2);
  252. final String initial = Styliser.stipControlCodes(bits[0]);
  253. if (initial.charAt(0) == '+') {
  254. // Channel Message
  255. final IRCChannelClientInfo newChannelClient = getChannelClient(
  256. channel, date, bits[0].substring(2, bits[0].length() - 1), joinNew);
  257. coreChannelHandler.onChannelMessage(parser, date, channel,
  258. newChannelClient, bits[1], host);
  259. return;
  260. } else if (initial.equalsIgnoreCase("***")) {
  261. // Some kind of state-changing action
  262. final String[] newBits = bits[1].split(" ");
  263. if (newBits.length > 2) {
  264. final IRCChannelClientInfo newChannelClient
  265. = getChannelClient(channel, date, newBits[0],
  266. joinNew);
  267. if (newBits[2].equalsIgnoreCase("joined")) {
  268. // User joined a relayed channel.
  269. if (!joinNew) {
  270. // If auto join on discover isn't enabled, we will
  271. // need to send this join, else it will already
  272. // have been sent.
  273. coreChannelHandler.onChannelJoin(parser, date,
  274. channel, newChannelClient);
  275. // And recolour to combat the nickcolour plugin
  276. // changing the colour on join.
  277. colourClient(newChannelClient);
  278. }
  279. return;
  280. } else if (newBits[2].equalsIgnoreCase("left")) {
  281. // User left a relayed channel.
  282. String reason = (newBits.length > 4) ? mergeBits(
  283. newBits, 4, newBits.length - 1, " ") : "()";
  284. reason = reason.substring(1, reason.length() - 1);
  285. coreChannelHandler.onChannelPart(parser, date, channel,
  286. newChannelClient, reason);
  287. removeChannelClient(channel, newBits[0]);
  288. return;
  289. } else if (newBits[2].equalsIgnoreCase("quit")) {
  290. // User quit a relayed channel.
  291. String reason = (newBits.length > 4) ? mergeBits(
  292. newBits, 4, newBits.length - 1, " ") : "()";
  293. reason = reason.substring(1, reason.length() - 1);
  294. coreChannelHandler.onChannelQuit(parser, date, channel,
  295. newChannelClient, reason);
  296. removeChannelClient(channel, newBits[0]);
  297. return;
  298. } else if (newBits[2].equalsIgnoreCase("kicked")) {
  299. // User was kicked from a relayed channel.
  300. String reason = (newBits.length > 7) ? mergeBits(
  301. newBits, 7, newBits.length - 1, " ") : "()";
  302. reason = reason.substring(1, reason.length() - 1);
  303. final IRCChannelClientInfo kickingChannelClient
  304. = (newBits.length > 6) ? getChannelClient(
  305. channel, date, newBits[6], joinNew) : null;
  306. coreChannelHandler.onChannelKick(parser, date, channel,
  307. newChannelClient, kickingChannelClient, reason,
  308. "");
  309. removeChannelClient(channel, newBits[0]);
  310. return;
  311. } else if (newBits[2].equalsIgnoreCase("now")
  312. && newBits.length > 3) {
  313. // User changed their nickname in a relayed channel.
  314. renameChannelClient(newChannelClient, newBits[3]);
  315. coreChannelHandler.onChannelNickChanged(parser, date,
  316. channel, newChannelClient, newBits[0]);
  317. return;
  318. }
  319. }
  320. } else if (initial.charAt(0) == '*') {
  321. // Channel Action
  322. final String[] newBits = bits[1].split(" ", 2);
  323. final IRCChannelClientInfo newChannelClient = getChannelClient(
  324. channel, date, newBits[0], joinNew);
  325. coreChannelHandler.onChannelAction(parser, date, channel,
  326. newChannelClient, newBits[1], "");
  327. return;
  328. }
  329. }
  330. // Pass it on unchanged.
  331. coreChannelHandler.onChannelMessage(parser, date, channel,
  332. channelClient, message, host);
  333. }
  334. /**
  335. * Merge the given bits.
  336. *
  337. * @param bits Bits to merge
  338. * @param start Start
  339. * @param end end
  340. * @param joiner What to use to join them
  341. * @return Joined bits.
  342. */
  343. private String mergeBits(final String[] bits, final int start,
  344. final int end, final String joiner) {
  345. final StringBuilder builder = new StringBuilder();
  346. for (int i = start; i <= end; i++) {
  347. if (bits.length < i) { break; }
  348. if (i != start) { builder.append(joiner); }
  349. builder.append(bits[i]);
  350. }
  351. return builder.toString();
  352. }
  353. /**
  354. * Send an onChannelNames() event for this channel.
  355. * This will cause all remote clients to vanish from the nicklist.
  356. */
  357. public void updateNames() {
  358. coreChannelHandler.onChannelGotNames(myChannel.getServer().getParser(),
  359. new Date(), myChannel.getChannelInfo());
  360. }
  361. }