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.

IRCChannelInfo.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /*
  2. * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
  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;
  23. import com.dmdirc.parser.common.ChannelListModeItem;
  24. import com.dmdirc.parser.interfaces.ChannelClientInfo;
  25. import com.dmdirc.parser.interfaces.ChannelInfo;
  26. import com.dmdirc.parser.interfaces.ClientInfo;
  27. import com.dmdirc.parser.interfaces.Parser;
  28. import com.dmdirc.parser.common.QueuePriority;
  29. import java.util.ArrayList;
  30. import java.util.Collection;
  31. import java.util.Hashtable;
  32. import java.util.HashMap;
  33. import java.util.LinkedList;
  34. import java.util.List;
  35. import java.util.Map;
  36. import java.util.Queue;
  37. /**
  38. * Contains Channel information.
  39. *
  40. * @author Shane Mc Cormack
  41. * @author Chris Smith
  42. * @see IRCParser
  43. */
  44. public class IRCChannelInfo implements ChannelInfo {
  45. /**
  46. * Boolean repreenting the status of names requests.
  47. * When this is false, any new names reply will cause current known channelclients to be removed.
  48. */
  49. private boolean bAddingNames = true;
  50. /** Unixtimestamp representing time when the channel was created. */
  51. private long nCreateTime = 0;
  52. /** Current known topic in the channel. */
  53. private String sTopic = "";
  54. /** Last known user to set the topic (Full host where possible). */
  55. private String sTopicUser = "";
  56. /** Unixtimestamp representing time when the topic was set. */
  57. private long nTopicTime = 0;
  58. /** Has this channel ever had a topic? */
  59. private boolean hadTopic = false;
  60. /** Known boolean-modes for channel. */
  61. private long nModes;
  62. /** Reference to the parser object that owns this channel, Used for modes. */
  63. private final IRCParser myParser; // Reference to parser object that owns this channel. Used for Modes
  64. /** Channel Name. */
  65. private final String sName;
  66. /** Hashtable containing references to ChannelClients. */
  67. private final Map<String, IRCChannelClientInfo> hChannelUserList = new Hashtable<String, IRCChannelClientInfo>();
  68. /** Hashtable storing values for modes set in the channel that use parameters. */
  69. private final Map<Character, String> hParamModes = new Hashtable<Character, String>();
  70. /** Hashtable storing list modes. */
  71. private final Map<Character, ArrayList<ChannelListModeItem>> hListModes = new Hashtable<Character, ArrayList<ChannelListModeItem>>();
  72. /**
  73. * LinkedList storing status of mode adding.
  74. * if an item is in this list for a mode, we are expecting new items for the list
  75. */
  76. private final List<Character> lAddingModes = new LinkedList<Character>();
  77. /** Modes waiting to be sent to the server. */
  78. private final List<String> lModeQueue = new LinkedList<String>();
  79. /** A Map to allow applications to attach misc data to this object */
  80. private Map myMap;
  81. /** Queue of requested list modes */
  82. private final Queue<Character> listModeQueue = new LinkedList<Character>();
  83. /** Listmode Queue Time */
  84. private long listModeQueueTime = System.currentTimeMillis();
  85. /** Have we asked the server for the list modes for this channel yet? */
  86. private boolean askedForListModes = false;
  87. /** Has OnChannelGotListModes ever been called for this channel? */
  88. private boolean hasGotListModes = false;
  89. /**
  90. * Create a new channel object.
  91. *
  92. * @param tParser Refernce to parser that owns this channelclient (used for modes)
  93. * @param name Channel name.
  94. */
  95. public IRCChannelInfo(final IRCParser tParser, final String name) {
  96. myMap = new HashMap<Object, Object>();
  97. myParser = tParser;
  98. sName = name;
  99. }
  100. /**
  101. * Get the listModeQueue.
  102. *
  103. * @return The listModeQueue
  104. */
  105. public Queue<Character> getListModeQueue() {
  106. Queue<Character> result = listModeQueue;
  107. final long now = System.currentTimeMillis();
  108. // Incase of breakage, if getListModeQueue() was last called greater than
  109. // 60 seconds ago, we reset the list.
  110. if (now-(30*1000) > listModeQueueTime) {
  111. result = new LinkedList<Character>();
  112. myParser.callDebugInfo(IRCParser.DEBUG_LMQ, "Resetting LMQ");
  113. }
  114. listModeQueueTime = now;
  115. return result;
  116. }
  117. /**
  118. * Ask the server for all the list modes for this channel.
  119. */
  120. public void requestListModes() {
  121. final IRCChannelClientInfo me = getChannelClient(myParser.getLocalClient());
  122. if (me == null) {
  123. // In a normal situation of non bouncer-brokenness this won't happen
  124. return;
  125. }
  126. askedForListModes = true;
  127. final String thisIRCD = myParser.getIRCD(true).toLowerCase();
  128. final boolean isFreenode = (thisIRCD.equals("hyperion") || thisIRCD.equals("dancer"));
  129. final boolean isUnreal = thisIRCD.equals("unreal");
  130. final boolean isStarChat = thisIRCD.equals("starchat");
  131. final boolean isHybrid = thisIRCD.equals("hybrid") || thisIRCD.equals("oftc-hybrid");
  132. final boolean isCharybdis = thisIRCD.equals("charybdis");
  133. // We are considered opped if we have a mode higher than voice (or if we have any modes if voice doesn't exist)
  134. long voiceValue = 0;
  135. if (myParser.prefixModes.get('v') != null) { voiceValue = myParser.prefixModes.get('v');}
  136. final boolean isOpped = me.getImportantModeValue() > voiceValue;
  137. int modecount = 1;
  138. if (!isUnreal && myParser.h005Info.containsKey("MODES")) {
  139. try {
  140. modecount = Integer.parseInt(myParser.h005Info.get("MODES"));
  141. } catch (NumberFormatException e) { /* use default modecount */}
  142. }
  143. // Support for potential future decent mode listing in the protocol
  144. //
  145. // See my proposal: http://shane.dmdirc.com/listmodes.php
  146. // Add listmode handler
  147. final boolean supportLISTMODE = myParser.h005Info.containsKey("LISTMODE");
  148. String listmodes = "";
  149. int i = 0;
  150. for (Character cTemp : myParser.chanModesOther.keySet()) {
  151. final int nTemp = myParser.chanModesOther.get(cTemp);
  152. if (nTemp == IRCParser.MODE_LIST) {
  153. if ((isFreenode || isHybrid || isCharybdis) && (cTemp == 'e' || cTemp == 'I') && !isOpped) {
  154. // IRCD doesn't allow non-ops to ask for these modes.
  155. continue;
  156. } else if (isStarChat && cTemp == 'H') {
  157. // IRCD Denies the mode exists
  158. continue;
  159. }
  160. i++;
  161. listmodes = listmodes + cTemp;
  162. if (i >= modecount && !supportLISTMODE) {
  163. myParser.sendString("MODE "+getName()+" "+listmodes, QueuePriority.LOW);
  164. i = 0;
  165. listmodes = "";
  166. }
  167. }
  168. }
  169. if (i > 0) {
  170. if (supportLISTMODE) {
  171. myParser.sendString("LISTMODE "+getName()+" "+listmodes, QueuePriority.LOW);
  172. } else {
  173. myParser.sendString("MODE "+getName()+" "+listmodes, QueuePriority.LOW);
  174. }
  175. }
  176. }
  177. /**
  178. * Has this channel ever had a topic? (even an empty one!)
  179. *
  180. * @return True if a topic has ever been known for this channel.
  181. */
  182. public synchronized boolean hadTopic() {
  183. return hadTopic;
  184. }
  185. /**
  186. * Change the value of hadTopic to true.
  187. */
  188. public synchronized void setHadTopic() {
  189. this.hadTopic = true;
  190. }
  191. /**
  192. * Have we ever asked the server for this channels listmodes?
  193. *
  194. * @return True if requestListModes() has ever been used, else false
  195. */
  196. public boolean hasAskedForListModes() {
  197. return askedForListModes;
  198. }
  199. /**
  200. * Returns true if OnChannelGotListModes ever been called for this channel.
  201. *
  202. * @return True if OnChannelGotListModes ever been called for this channel.
  203. */
  204. public boolean hasGotListModes() {
  205. return hasGotListModes;
  206. }
  207. /**
  208. * Set if OnChannelGotListModes ever been called for this channel.
  209. *
  210. * @param newValue new value for if OnChannelGotListModes ever been called for this channel.
  211. */
  212. protected void setHasGotListModes(final boolean newValue) {
  213. hasGotListModes = newValue;
  214. }
  215. /**
  216. * Set the Map object attatched to this object.
  217. *
  218. * @param newMap New Map to attatch.
  219. */
  220. public void setMap(final Map newMap) {
  221. myMap = newMap;
  222. }
  223. /**
  224. * Get the Map object attatched to this object.
  225. *
  226. * @return Map to attatched to this.
  227. */
  228. public Map getMap() {
  229. return myMap;
  230. }
  231. /**
  232. * Set if we are getting a names request or not.
  233. *
  234. * @param newValue if false, any new names reply will cause current known channelclients to be removed.
  235. */
  236. public void setAddingNames(final boolean newValue) { bAddingNames = newValue; }
  237. /**
  238. * Get if we are getting a names request or not.
  239. *
  240. * @return if false, any new names reply will cause current known channelclients to be removed.
  241. */
  242. public boolean isAddingNames() { return bAddingNames; }
  243. /** {@inheritDoc} */
  244. @Override
  245. public String getName() { return sName; }
  246. /** {@inheritDoc} */
  247. @Override
  248. public int getChannelClientCount() { return hChannelUserList.size(); }
  249. /** {@inheritDoc} */
  250. @Override
  251. public Collection<ChannelClientInfo> getChannelClients() {
  252. return new ArrayList<ChannelClientInfo>(hChannelUserList.values());
  253. }
  254. /**
  255. * Empty the channel (Remove all known channelclients).
  256. */
  257. protected void emptyChannel() {
  258. IRCClientInfo cTemp = null;
  259. for (IRCChannelClientInfo client : hChannelUserList.values()) {
  260. cTemp = client.getClient();
  261. cTemp.delChannelClientInfo(client);
  262. if (cTemp != myParser.getLocalClient() && !cTemp.checkVisibility()) {
  263. myParser.removeClient(cTemp);
  264. }
  265. }
  266. hChannelUserList.clear();
  267. }
  268. /** {@inheritDoc} */
  269. @Override
  270. public IRCChannelClientInfo getChannelClient(final String sWho) {
  271. return getChannelClient(sWho, false);
  272. }
  273. /** {@inheritDoc} */
  274. @Override
  275. public IRCChannelClientInfo getChannelClient(final String sWho, final boolean createFake) {
  276. final String who = myParser.getStringConverter().toLowerCase(IRCClientInfo.parseHost(sWho));
  277. if (hChannelUserList.containsKey(who)) {
  278. return hChannelUserList.get(who);
  279. }
  280. if (createFake) {
  281. return new IRCChannelClientInfo(myParser, (new IRCClientInfo(myParser, sWho)).setFake(true), this);
  282. } else {
  283. return null;
  284. }
  285. }
  286. /** {@inheritDoc} */
  287. @Override
  288. public IRCChannelClientInfo getChannelClient(final ClientInfo cWho) {
  289. for (IRCChannelClientInfo client : hChannelUserList.values()) {
  290. if (client.getClient() == cWho) {
  291. return client;
  292. }
  293. }
  294. return null;
  295. }
  296. /**
  297. * Get the ChannelClientInfo object associated with a ClientInfo object.
  298. *
  299. * @param cClient Client object to be added to channel
  300. * @return ChannelClientInfo object added, or an existing object if already known on channel
  301. */
  302. protected IRCChannelClientInfo addClient(final IRCClientInfo cClient) {
  303. IRCChannelClientInfo cTemp = getChannelClient(cClient);
  304. if (cTemp == null) {
  305. cTemp = new IRCChannelClientInfo(myParser, cClient, this);
  306. hChannelUserList.put(myParser.getStringConverter().toLowerCase(cTemp.getClient().getNickname()), cTemp);
  307. }
  308. return cTemp;
  309. }
  310. /**
  311. * Remove ChannelClientInfo object associated with a ClientInfo object.
  312. *
  313. * @param cClient Client object to be removed from channel
  314. */
  315. protected void delClient(final IRCClientInfo cClient) {
  316. IRCChannelClientInfo cTemp = getChannelClient(cClient);
  317. if (cTemp != null) {
  318. final IRCClientInfo clTemp = cTemp.getClient();
  319. clTemp.delChannelClientInfo(cTemp);
  320. if (clTemp != myParser.getLocalClient() && !clTemp.checkVisibility()) {
  321. myParser.removeClient(clTemp);
  322. }
  323. hChannelUserList.remove(myParser.getStringConverter().toLowerCase(cTemp.getClient().getNickname()));
  324. }
  325. }
  326. /**
  327. * Rename a channelClient.
  328. *
  329. * @param oldNickname Nickname client used to be known as
  330. * @param cChannelClient ChannelClient object with updated client object
  331. */
  332. protected void renameClient(final String oldNickname, final IRCChannelClientInfo cChannelClient) {
  333. if (hChannelUserList.containsKey(oldNickname)) {
  334. final IRCChannelClientInfo cTemp = hChannelUserList.get(oldNickname);
  335. if (cTemp == cChannelClient) {
  336. // Remove the old key
  337. hChannelUserList.remove(oldNickname);
  338. // Add with the new key. (getNickname will return the new name not the
  339. // old one)
  340. hChannelUserList.put(myParser.getStringConverter().toLowerCase(cTemp.getClient().getNickname()), cTemp);
  341. }
  342. }
  343. }
  344. /**
  345. * Set the create time.
  346. *
  347. * @param nNewTime New unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
  348. */
  349. protected void setCreateTime(final long nNewTime) { nCreateTime = nNewTime; }
  350. /**
  351. * Get the Create time.
  352. *
  353. * @return Unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
  354. */
  355. public long getCreateTime() { return nCreateTime; }
  356. /**
  357. * Set the topic time.
  358. *
  359. * @param nNewTime New unixtimestamp time for the topic (Seconds since epoch, not milliseconds)
  360. */
  361. protected void setTopicTime(final long nNewTime) { nTopicTime = nNewTime; }
  362. /** {@inheritDoc} */
  363. @Override
  364. public long getTopicTime() { return nTopicTime; }
  365. /**
  366. * Set the topic.
  367. *
  368. * @param sNewTopic New contents of topic
  369. */
  370. protected void setInternalTopic(final String sNewTopic) { sTopic = sNewTopic; }
  371. /** {@inheritDoc} */
  372. @Override
  373. public String getTopic() { return sTopic; }
  374. /**
  375. * Set the topic creator.
  376. *
  377. * @param sNewUser New user who set the topic (nickname if gotten on connect, full host if seen by parser)
  378. */
  379. protected void setTopicUser(final String sNewUser) { sTopicUser = sNewUser; }
  380. /** {@inheritDoc} */
  381. @Override
  382. public String getTopicSetter() { return sTopicUser; }
  383. /**
  384. * Set the channel modes (as an integer).
  385. *
  386. * @param nNewMode new long representing channel modes. (Boolean only)
  387. */
  388. protected void setMode(final long nNewMode) { nModes = nNewMode; }
  389. /**
  390. * Get the channel modes (as an integer).
  391. *
  392. * @return long representing channel modes. (Boolean only)
  393. */
  394. public long getMode() { return nModes; }
  395. /** {@inheritDoc} */
  396. @Override
  397. public String getModes() {
  398. final StringBuilder sModes = new StringBuilder("+");
  399. final StringBuilder sModeParams = new StringBuilder();
  400. String sTemp = "";
  401. long nTemp = 0;
  402. final long nChanModes = this.getMode();
  403. for (char cTemp : myParser.chanModesBool.keySet()) {
  404. nTemp = myParser.chanModesBool.get(cTemp);
  405. if ((nChanModes & nTemp) == nTemp) { sModes.append(cTemp); }
  406. }
  407. for (char cTemp : hParamModes.keySet()) {
  408. sTemp = hParamModes.get(cTemp);
  409. if (!sTemp.isEmpty()) {
  410. sModes.append(cTemp);
  411. sModeParams.append(" ").append(this.getMode(cTemp));
  412. }
  413. }
  414. return sModes.append(sModeParams).toString();
  415. }
  416. /**
  417. * Set a channel mode that requires a parameter.
  418. *
  419. * @param cMode Character representing mode
  420. * @param sValue String repreenting value (if "" mode is unset)
  421. */
  422. protected void setModeParam(final Character cMode, final String sValue) {
  423. if (sValue.isEmpty()) {
  424. if (hParamModes.containsKey(cMode)) {
  425. hParamModes.remove(cMode);
  426. }
  427. } else {
  428. hParamModes.put(cMode, sValue);
  429. }
  430. }
  431. /** {@inheritDoc} */
  432. @Override
  433. public String getMode(final char cMode) {
  434. if (hParamModes.containsKey(cMode)) {
  435. return hParamModes.get(cMode);
  436. }
  437. return "";
  438. }
  439. /**
  440. * Add/Remove a value to a channel list.
  441. *
  442. * @param givenMode Character representing mode
  443. * @param givenItem ChannelListModeItem representing the item
  444. * @param bAdd Add or remove the value. (true for add, false for remove)
  445. */
  446. protected void setListModeParam(final Character givenMode, final ChannelListModeItem givenItem, final boolean bAdd) {
  447. Character cMode = givenMode;
  448. ChannelListModeItem newItem = givenItem;
  449. if (!myParser.chanModesOther.containsKey(cMode) || myParser.chanModesOther.get(cMode) != IRCParser.MODE_LIST) { return; }
  450. // Hyperion sucks.
  451. if (cMode == 'b' || cMode == 'q') {
  452. final String thisIRCD = myParser.getIRCD(true).toLowerCase();
  453. if ((thisIRCD.equals("hyperion") || thisIRCD.equals("dancer"))) {
  454. if (cMode == 'b' && givenItem.getItem().charAt(0) == '%') {
  455. cMode = 'q';
  456. } else if (cMode == 'q' && givenItem.getItem().charAt(0) != '%') {
  457. cMode = 'b';
  458. }
  459. if (givenItem.getItem().charAt(0) == '%') {
  460. newItem = new ChannelListModeItem(givenItem.getItem().substring(1), givenItem.getOwner(), givenItem.getTime());
  461. }
  462. }
  463. }
  464. if (!hListModes.containsKey(cMode)) {
  465. hListModes.put(cMode, new ArrayList<ChannelListModeItem>());
  466. }
  467. final ArrayList<ChannelListModeItem> lModes = hListModes.get(cMode);
  468. for (int i = 0; i < lModes.size(); i++) {
  469. if (myParser.getStringConverter().equalsIgnoreCase(lModes.get(i).getItem(), newItem.getItem())) {
  470. if (bAdd) { return; }
  471. else {
  472. lModes.remove(i);
  473. break;
  474. }
  475. }
  476. }
  477. if (bAdd) { lModes.add(newItem); }
  478. }
  479. /** {@inheritDoc} */
  480. @Override
  481. public Collection<ChannelListModeItem> getListMode(final char cMode) {
  482. if (!myParser.chanModesOther.containsKey(cMode) || myParser.chanModesOther.get(cMode) != IRCParser.MODE_LIST) { return null; }
  483. if (!hListModes.containsKey(cMode)) {
  484. hListModes.put(cMode, new ArrayList<ChannelListModeItem>());
  485. }
  486. return hListModes.get(cMode);
  487. }
  488. /**
  489. * Get the "adding state" of a list mode.
  490. *
  491. * @param cMode Character representing mode
  492. * @return false if we are not expecting a 367 etc, else true.
  493. */
  494. public boolean getAddState(final Character cMode) {
  495. synchronized (lAddingModes) {
  496. return lAddingModes.contains(cMode);
  497. }
  498. }
  499. /**
  500. * Get the "adding state" of a list mode.
  501. *
  502. * @param cMode Character representing mode
  503. * @param newState change the value returned by getAddState
  504. */
  505. protected void setAddState(final Character cMode, final boolean newState) {
  506. synchronized (lAddingModes) {
  507. if (newState) {
  508. lAddingModes.add(cMode);
  509. } else {
  510. if (lAddingModes.contains(cMode)) { lAddingModes.remove(cMode); }
  511. }
  512. }
  513. }
  514. /**
  515. * Reset the "adding state" of *all* list modes.
  516. */
  517. protected void resetAddState() {
  518. synchronized (lAddingModes) {
  519. lAddingModes.clear();
  520. }
  521. }
  522. /** {@inheritDoc} */
  523. @Override
  524. public void alterMode(final boolean positive, final Character mode, final String parameter) {
  525. int modecount = 1;
  526. int modeint = 0;
  527. String modestr = "";
  528. if (myParser.h005Info.containsKey("MODES")) {
  529. try {
  530. modecount = Integer.parseInt(myParser.h005Info.get("MODES"));
  531. } catch (NumberFormatException e) {
  532. modecount = 1;
  533. }
  534. }
  535. if (!myParser.isUserSettable(mode)) { return; }
  536. modestr = ((positive) ? "+" : "-") + mode;
  537. if (myParser.chanModesBool.containsKey(mode)) {
  538. final String teststr = ((positive) ? "-" : "+") + mode;
  539. if (lModeQueue.contains(teststr)) {
  540. lModeQueue.remove(teststr);
  541. return;
  542. } else if (lModeQueue.contains(modestr)) {
  543. return;
  544. }
  545. } else {
  546. // May need a param
  547. if (myParser.prefixModes.containsKey(mode)) {
  548. modestr = modestr + " " + parameter;
  549. } else {
  550. modeint = myParser.chanModesOther.get(mode);
  551. if ((modeint & IRCParser.MODE_LIST) == IRCParser.MODE_LIST) {
  552. modestr = modestr + " " + parameter;
  553. } else if (!positive && ((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
  554. modestr = modestr + " " + parameter;
  555. } else if (positive && ((modeint & IRCParser.MODE_SET) == IRCParser.MODE_SET)) {
  556. // Does mode require a param to unset aswell?
  557. // We might need to queue an unset first
  558. if (((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
  559. final String existingParam = getMode(mode);
  560. if (!existingParam.isEmpty()) {
  561. final String reverseModeStr = "-" + mode + " " + existingParam;
  562. myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", reverseModeStr);
  563. lModeQueue.add(reverseModeStr);
  564. if (lModeQueue.size() == modecount) { flushModes(); }
  565. }
  566. }
  567. modestr = modestr + " " + parameter;
  568. }
  569. }
  570. }
  571. myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", modestr);
  572. lModeQueue.add(modestr);
  573. if (lModeQueue.size() == modecount) { flushModes(); }
  574. }
  575. /** {@inheritDoc} */
  576. @Override
  577. public void flushModes() {
  578. if (lModeQueue.isEmpty()) { return; }
  579. final StringBuilder positivemode = new StringBuilder();
  580. final StringBuilder positiveparam = new StringBuilder();
  581. final StringBuilder negativemode = new StringBuilder();
  582. final StringBuilder negativeparam = new StringBuilder();
  583. final StringBuilder sendModeStr = new StringBuilder();
  584. String modestr;
  585. String[] modeparam;
  586. boolean positive;
  587. for (int i = 0; i < lModeQueue.size(); ++i) {
  588. modeparam = lModeQueue.get(i).split(" ");
  589. modestr = modeparam[0];
  590. positive = modestr.charAt(0) == '+';
  591. if (positive) {
  592. positivemode.append(modestr.charAt(1));
  593. if (modeparam.length > 1) { positiveparam.append(" ").append(modeparam[1]); }
  594. } else {
  595. negativemode.append(modestr.charAt(1));
  596. if (modeparam.length > 1) { negativeparam.append(" ").append(modeparam[1]); }
  597. }
  598. }
  599. if (negativemode.length() > 0) { sendModeStr.append("-").append(negativemode); }
  600. if (positivemode.length() > 0) { sendModeStr.append("+").append(positivemode); }
  601. if (negativeparam.length() > 0) { sendModeStr.append(negativeparam); }
  602. if (positiveparam.length() > 0) { sendModeStr.append(positiveparam); }
  603. myParser.callDebugInfo(IRCParser.DEBUG_INFO, "Sending mode: %s", sendModeStr.toString());
  604. myParser.sendRawMessage("MODE " + sName + " " + sendModeStr.toString());
  605. clearModeQueue();
  606. }
  607. /**
  608. * This function will clear the mode queue (WITHOUT Sending).
  609. */
  610. public void clearModeQueue() {
  611. lModeQueue.clear();
  612. }
  613. /** {@inheritDoc} */
  614. @Override
  615. public void sendMessage(final String message) {
  616. if (message.isEmpty()) { return; }
  617. myParser.sendString("PRIVMSG " + sName + " :" + message);
  618. }
  619. /**
  620. * Send a notice message to a target.
  621. *
  622. * @param sMessage Message to send
  623. */
  624. public void sendNotice(final String sMessage) {
  625. if (sMessage.isEmpty()) { return; }
  626. myParser.sendString("NOTICE " + sName + " :" + sMessage);
  627. }
  628. /** {@inheritDoc} */
  629. @Override
  630. public void sendAction(final String action) {
  631. if (action.isEmpty()) { return; }
  632. sendCTCP("ACTION", action);
  633. }
  634. /**
  635. * Send a CTCP to a target.
  636. *
  637. * @param sType Type of CTCP
  638. * @param sMessage Optional Additional Parameters
  639. */
  640. public void sendCTCP(final String sType, String sMessage) {
  641. if (sType.isEmpty()) { return; }
  642. final char char1 = (char) 1;
  643. if (!sMessage.isEmpty()) { sMessage = " " + sMessage; }
  644. sendMessage(char1 + sType.toUpperCase() + sMessage + char1);
  645. }
  646. /**
  647. * Send a CTCPReply to a target.
  648. *
  649. * @param sType Type of CTCP
  650. * @param sMessage Optional Additional Parameters
  651. */
  652. public void sendCTCPReply(final String sType, String sMessage) {
  653. if (sType.isEmpty()) { return; }
  654. final char char1 = (char) 1;
  655. if (!sMessage.isEmpty()) { sMessage = " " + sMessage; }
  656. sendNotice(char1 + sType.toUpperCase() + sMessage + char1);
  657. }
  658. /**
  659. * Get a string representation of the Channel.
  660. *
  661. * @return String representation of the Channel.
  662. */
  663. @Override
  664. public String toString() { return sName; }
  665. /** {@inheritDoc} */
  666. @Override
  667. public Parser getParser() { return myParser; }
  668. /** {@inheritDoc} */
  669. @Override
  670. public void part(final String reason) {
  671. myParser.partChannel(sName, reason);
  672. }
  673. /** {@inheritDoc} */
  674. @Override
  675. public void setTopic(final String topic) {
  676. myParser.sendRawMessage("TOPIC " + sName + " :" + topic);
  677. }
  678. /** {@inheritDoc} */
  679. @Override
  680. public void sendWho() {
  681. myParser.sendRawMessage("WHO " + sName);
  682. }
  683. }