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

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