You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProcessMode.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * Copyright (c) 2006-2017 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.parser.irc.processors;
  23. import com.dmdirc.parser.common.ChannelListModeItem;
  24. import com.dmdirc.parser.common.ParserError;
  25. import com.dmdirc.parser.events.ChannelModeChangeEvent;
  26. import com.dmdirc.parser.events.ChannelNonUserModeChangeEvent;
  27. import com.dmdirc.parser.events.ChannelSingleModeChangeEvent;
  28. import com.dmdirc.parser.events.ChannelUserModeChangeEvent;
  29. import com.dmdirc.parser.events.UserModeChangeEvent;
  30. import com.dmdirc.parser.events.UserModeDiscoveryEvent;
  31. import com.dmdirc.parser.interfaces.ChannelClientInfo;
  32. import com.dmdirc.parser.interfaces.ChannelInfo;
  33. import com.dmdirc.parser.interfaces.ClientInfo;
  34. import com.dmdirc.parser.irc.IRCChannelClientInfo;
  35. import com.dmdirc.parser.irc.IRCChannelInfo;
  36. import com.dmdirc.parser.irc.IRCClientInfo;
  37. import com.dmdirc.parser.irc.IRCParser;
  38. import com.dmdirc.parser.irc.ModeManager;
  39. import com.dmdirc.parser.irc.PrefixModeManager;
  40. import com.dmdirc.parser.irc.TimestampedIRCProcessor;
  41. import java.time.LocalDateTime;
  42. import java.util.Calendar;
  43. import javax.inject.Inject;
  44. import javax.inject.Named;
  45. /**
  46. * Process a Mode line.
  47. */
  48. public class ProcessMode extends TimestampedIRCProcessor {
  49. /** The manager to use to access prefix modes. */
  50. private final PrefixModeManager prefixModeManager;
  51. /** Mode manager to use for user modes. */
  52. private final ModeManager userModeManager;
  53. /** Mode manager to use for channel modes. */
  54. private final ModeManager chanModeManager;
  55. /**
  56. * Create a new instance of the IRCProcessor Object.
  57. *
  58. * @param parser IRCParser That owns this IRCProcessor
  59. * @param prefixModeManager The manager to use to access prefix modes.
  60. * @param userModeManager Mode manager to use for user modes.
  61. * @param chanModeManager Mode manager to use for channel modes.
  62. */
  63. @Inject
  64. public ProcessMode(final IRCParser parser, final PrefixModeManager prefixModeManager,
  65. @Named("user") final ModeManager userModeManager,
  66. @Named("channel") final ModeManager chanModeManager) {
  67. super(parser, "MODE", "324", "221");
  68. this.prefixModeManager = prefixModeManager;
  69. this.userModeManager = userModeManager;
  70. this.chanModeManager = chanModeManager;
  71. }
  72. /**
  73. * Process a Mode Line.
  74. *
  75. * @param date The LocalDateTime that this event occurred at.
  76. * @param sParam Type of line to process ("MODE", "324")
  77. * @param token IRCTokenised line to process
  78. */
  79. @Override
  80. public void process(final LocalDateTime date, final String sParam, final String... token) {
  81. final String[] sModestr;
  82. final String sChannelName;
  83. switch (sParam) {
  84. case "324":
  85. sChannelName = token[3];
  86. sModestr = new String[token.length - 4];
  87. System.arraycopy(token, 4, sModestr, 0, token.length - 4);
  88. break;
  89. case "221":
  90. processUserMode(date, sParam, token, new String[]{token[token.length - 1]}, true);
  91. return;
  92. default:
  93. sChannelName = token[2];
  94. sModestr = new String[token.length - 3];
  95. System.arraycopy(token, 3, sModestr, 0, token.length - 3);
  96. break;
  97. }
  98. if (isValidChannelName(sChannelName)) {
  99. processChanMode(date, sParam, token, sModestr, sChannelName);
  100. } else {
  101. processUserMode(date, sParam, token, sModestr, false);
  102. }
  103. }
  104. /**
  105. * Method to trim spaces from strings.
  106. *
  107. * @param str String to trim
  108. * @return String without spaces on the ends
  109. */
  110. private String trim(final String str) {
  111. return str.trim();
  112. }
  113. /**
  114. * Process Chan modes.
  115. *
  116. * @param date The LocalDateTime that this event occurred at.
  117. * @param sParam String representation of parameter to parse
  118. * @param token IRCTokenised Array of the incomming line
  119. * @param sModestr The modes and params
  120. * @param sChannelName Channel these modes are for
  121. */
  122. public void processChanMode(final LocalDateTime date, final String sParam, final String[] token, final String[] sModestr, final String sChannelName) {
  123. final StringBuilder sFullModeStr = new StringBuilder();
  124. final IRCChannelInfo iChannel = getChannel(sChannelName);
  125. if (iChannel == null) {
  126. return;
  127. }
  128. // Get the current channel modes
  129. String nCurrent = "";
  130. if (!"324".equals(sParam)) {
  131. nCurrent = iChannel.getMode();
  132. }
  133. final IRCChannelClientInfo setterCCI = iChannel.getChannelClient(token[0], true);
  134. // Facilitate dmdirc formatter
  135. if (IRCParser.ALWAYS_UPDATECLIENT && setterCCI != null && setterCCI.getClient().getHostname().isEmpty()) {
  136. setterCCI.getClient().setUserBits(token[0], false);
  137. }
  138. // Loop through the mode string, and add/remove modes/params where they are needed
  139. char cPositive = '+';
  140. boolean bPositive = true;
  141. long nValue = 0;
  142. int nParam = 1;
  143. final StringBuilder sNonUserModeStrParams = new StringBuilder();
  144. final StringBuilder sNonUserModeStr = new StringBuilder();
  145. for (int i = 0; i < sModestr[0].length(); ++i) {
  146. final Character cMode = sModestr[0].charAt(i);
  147. if (cMode.equals(":".charAt(0))) {
  148. continue;
  149. }
  150. sNonUserModeStr.append(cMode);
  151. if (cMode.equals("+".charAt(0))) {
  152. cPositive = '+';
  153. bPositive = true;
  154. } else if (cMode.equals("-".charAt(0))) {
  155. cPositive = '-';
  156. bPositive = false;
  157. } else {
  158. final boolean bBooleanMode;
  159. final String sModeParam;
  160. if (chanModeManager.isMode(cMode)) {
  161. bBooleanMode = true;
  162. } else if (parser.chanModesOther.containsKey(cMode)) {
  163. nValue = parser.chanModesOther.get(cMode);
  164. bBooleanMode = false;
  165. } else if (prefixModeManager.isPrefixMode(cMode)) {
  166. // (de) OP/Voice someone
  167. if (sModestr.length <= nParam) {
  168. parser.callErrorInfo(new ParserError(ParserError.ERROR_FATAL + ParserError.ERROR_USER, "Broken Modes. Parameter required but not given.", parser.getLastLine()));
  169. return;
  170. }
  171. sModeParam = sModestr[nParam++];
  172. callDebugInfo(IRCParser.DEBUG_INFO, "User Mode: %c / %s {Positive: %b}",
  173. cMode, sModeParam, bPositive);
  174. final IRCChannelClientInfo iChannelClientInfo = iChannel.getChannelClient(sModeParam);
  175. if (iChannelClientInfo == null) {
  176. // Client not known?
  177. callDebugInfo(IRCParser.DEBUG_INFO, "User Mode for client not on channel." +
  178. " Ignoring (%s)", sModeParam);
  179. continue;
  180. }
  181. callDebugInfo(IRCParser.DEBUG_INFO, "\tOld Mode Value: %s",
  182. iChannelClientInfo.getAllModes());
  183. if (bPositive) {
  184. iChannelClientInfo.addMode(cMode);
  185. } else {
  186. iChannelClientInfo.removeMode(cMode);
  187. }
  188. callChannelUserModeChanged(date, iChannel, iChannelClientInfo, setterCCI, token[0],
  189. (bPositive ? "+" : "-") + cMode);
  190. continue;
  191. } else {
  192. // unknown mode - add as boolean
  193. chanModeManager.add(cMode);
  194. bBooleanMode = true;
  195. }
  196. if (bBooleanMode) {
  197. callDebugInfo(IRCParser.DEBUG_INFO, "Boolean Mode: %c {Positive: %b}", cMode, bPositive);
  198. if (bPositive) {
  199. nCurrent = chanModeManager.insertMode(nCurrent, cMode);
  200. } else {
  201. nCurrent = chanModeManager.removeMode(nCurrent, cMode);
  202. }
  203. } else {
  204. if ((bPositive || nValue == IRCParser.MODE_LIST ||
  205. (nValue & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET) &&
  206. sModestr.length <= nParam) {
  207. parser.callErrorInfo(new ParserError(ParserError.ERROR_FATAL + ParserError.ERROR_USER, "Broken Modes. Parameter required but not given.", parser.getLastLine()));
  208. continue;
  209. }
  210. if (nValue == IRCParser.MODE_LIST) {
  211. // List Mode
  212. sModeParam = sModestr[nParam++];
  213. sNonUserModeStrParams.append(' ').append(sModeParam);
  214. final long nTemp = Calendar.getInstance().getTimeInMillis() / 1000;
  215. iChannel.setListModeParam(cMode, new ChannelListModeItem(sModeParam, token[0], nTemp), bPositive);
  216. callDebugInfo(IRCParser.DEBUG_INFO, "List Mode: %c [%s] {Positive: %b}", cMode, sModeParam, bPositive);
  217. if (!"324".equals(sParam)) {
  218. getCallbackManager().publish(
  219. new ChannelSingleModeChangeEvent(
  220. parser, LocalDateTime.now(), iChannel,
  221. setterCCI, token[0], cPositive + cMode + " " +
  222. sModeParam));
  223. }
  224. } else {
  225. // Mode with a parameter
  226. if (bPositive) {
  227. // +Mode - always needs a parameter to set
  228. sModeParam = sModestr[nParam++];
  229. sNonUserModeStrParams.append(' ').append(sModeParam);
  230. callDebugInfo(IRCParser.DEBUG_INFO, "Set Mode: %c [%s] {Positive: %b}", cMode, sModeParam, bPositive);
  231. iChannel.setModeParam(cMode, sModeParam);
  232. if (!"324".equals(sParam)) {
  233. getCallbackManager().publish(
  234. new ChannelSingleModeChangeEvent(
  235. parser, LocalDateTime.now(),
  236. iChannel, setterCCI, token[0],
  237. cPositive + cMode + " " +
  238. sModeParam));
  239. }
  240. } else {
  241. // -Mode - parameter isn't always needed, we need to check
  242. if ((nValue & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET) {
  243. sModeParam = sModestr[nParam++];
  244. sNonUserModeStrParams.append(' ').append(sModeParam);
  245. } else {
  246. sModeParam = "";
  247. }
  248. callDebugInfo(IRCParser.DEBUG_INFO, "Unset Mode: %c [%s] {Positive: %b}", cMode, sModeParam, bPositive);
  249. iChannel.setModeParam(cMode, "");
  250. if (!"324".equals(sParam)) {
  251. getCallbackManager().publish(
  252. new ChannelSingleModeChangeEvent(
  253. parser, LocalDateTime.now(),
  254. iChannel, setterCCI, token[0],
  255. trim(cPositive + cMode + " " + sModeParam)));
  256. }
  257. }
  258. }
  259. }
  260. }
  261. }
  262. // Call Callbacks
  263. for (String aSModestr : sModestr) {
  264. sFullModeStr.append(aSModestr).append(' ');
  265. }
  266. iChannel.setMode(nCurrent);
  267. if ("324".equals(sParam)) {
  268. callChannelModeChanged(date, iChannel, setterCCI, "", sFullModeStr.toString().trim());
  269. } else {
  270. callChannelModeChanged(date, iChannel, setterCCI, token[0], sFullModeStr.toString().trim());
  271. getCallbackManager().publish(
  272. new ChannelNonUserModeChangeEvent(parser, LocalDateTime.now(), iChannel,
  273. setterCCI, token[0],
  274. trim(sNonUserModeStr.toString() + sNonUserModeStrParams)));
  275. }
  276. }
  277. /**
  278. * Process user modes.
  279. *
  280. * @param date The LocalDateTime that this event occurred at.
  281. * @param sParam String representation of parameter to parse
  282. * @param token IRCTokenised Array of the incomming line
  283. * @param clearOldModes Clear old modes before applying these modes (used by 221)
  284. */
  285. private void processUserMode(final LocalDateTime date, final String sParam, final String[] token, final String[] sModestr,
  286. final boolean clearOldModes) {
  287. final IRCClientInfo iClient = getClientInfo(token[2]);
  288. if (iClient == null) {
  289. return;
  290. }
  291. String nCurrent;
  292. if (clearOldModes) {
  293. nCurrent = "";
  294. } else {
  295. nCurrent = iClient.getUserMode();
  296. }
  297. boolean bPositive = true;
  298. for (int i = 0; i < sModestr[0].length(); ++i) {
  299. final Character cMode = sModestr[0].charAt(i);
  300. if (cMode.equals("+".charAt(0))) {
  301. bPositive = true;
  302. } else if (cMode.equals("-".charAt(0))) {
  303. bPositive = false;
  304. } else if (!cMode.equals(":".charAt(0))) {
  305. if (!userModeManager.isMode(cMode)) {
  306. // Unknown mode
  307. callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got unknown user mode " + cMode + " - Added", parser.getLastLine()));
  308. userModeManager.add(cMode);
  309. }
  310. // Usermodes are always boolean
  311. callDebugInfo(IRCParser.DEBUG_INFO, "User Mode: %c {Positive: %b}", cMode, bPositive);
  312. if (bPositive) {
  313. nCurrent = userModeManager.insertMode(nCurrent, cMode);
  314. } else {
  315. nCurrent = userModeManager.removeMode(nCurrent, cMode);
  316. }
  317. }
  318. }
  319. iClient.setUserMode(nCurrent);
  320. if ("221".equals(sParam)) {
  321. callUserModeDiscovered(date, iClient, sModestr[0]);
  322. } else {
  323. callUserModeChanged(date, iClient, token[0], sModestr[0]);
  324. }
  325. }
  326. /**
  327. * Callback to all objects implementing the ChannelModeChanged Callback.
  328. *
  329. * @param date The LocalDateTime that this event occurred at.
  330. * @param cChannel Channel where modes were changed
  331. * @param cChannelClient Client chaning the modes (null if server)
  332. * @param sHost Host doing the mode changing (User host or server name)
  333. * @param sModes Exact String parsed
  334. */
  335. protected void callChannelModeChanged(final LocalDateTime date, final ChannelInfo cChannel,
  336. final ChannelClientInfo cChannelClient, final String sHost, final String sModes) {
  337. getCallbackManager().publish(
  338. new ChannelModeChangeEvent(parser, date, cChannel, cChannelClient,
  339. sHost, sModes));
  340. }
  341. /**
  342. * Callback to all objects implementing the ChannelUserModeChanged Callback.
  343. *
  344. * @param date The LocalDateTime that this event occurred at.
  345. * @param cChannel Channel where modes were changed
  346. * @param cChangedClient Client being changed
  347. * @param cSetByClient Client chaning the modes (null if server)
  348. * @param sHost Host doing the mode changing (User host or server name)
  349. * @param sMode String representing mode change (ie +o)
  350. */
  351. protected void callChannelUserModeChanged(final LocalDateTime date, final ChannelInfo cChannel,
  352. final ChannelClientInfo cChangedClient, final ChannelClientInfo cSetByClient,
  353. final String sHost, final String sMode) {
  354. getCallbackManager().publish(
  355. new ChannelUserModeChangeEvent(parser, date, cChannel,
  356. cChangedClient, cSetByClient, sHost, sMode));
  357. }
  358. /**
  359. * Callback to all objects implementing the UserModeChanged Callback.
  360. *
  361. * @param date The LocalDateTime that this event occurred at.
  362. * @param cClient Client that had the mode changed (almost always us)
  363. * @param sSetby Host that set the mode (us or servername)
  364. * @param sModes The modes set.
  365. */
  366. protected void callUserModeChanged(final LocalDateTime date, final ClientInfo cClient, final String sSetby,
  367. final String sModes) {
  368. getCallbackManager().publish(
  369. new UserModeChangeEvent(parser, date, cClient, sSetby, sModes));
  370. }
  371. /**
  372. * Callback to all objects implementing the UserModeDiscovered Callback.
  373. *
  374. * @param date The LocalDateTime that this event occurred at.
  375. * @param cClient Client that had the mode changed (almost always us)
  376. * @param sModes The modes set.
  377. */
  378. protected void callUserModeDiscovered(final LocalDateTime date, final ClientInfo cClient, final String sModes) {
  379. getCallbackManager().publish(
  380. new UserModeDiscoveryEvent(parser, date, cClient, sModes));
  381. }
  382. }