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 28KB

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