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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. /*
  2. * Copyright (c) 2006-2013 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.parser.irc;
  23. import com.dmdirc.parser.common.ChannelListModeItem;
  24. import com.dmdirc.parser.common.ParserError;
  25. import com.dmdirc.parser.common.QueuePriority;
  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.Parser;
  30. import java.util.ArrayList;
  31. import java.util.Collection;
  32. import java.util.Collections;
  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. * @see IRCParser
  42. */
  43. public class IRCChannelInfo implements ChannelInfo {
  44. /**
  45. * Boolean repreenting the status of names requests.
  46. * When this is false, any new names reply will cause current known channelclients to be removed.
  47. */
  48. private boolean addingNames = true;
  49. /** Unixtimestamp representing time when the channel was created. */
  50. private long creationTime;
  51. /** Current known topic in the channel. */
  52. private String topic = "";
  53. /** Last known user to set the topic (Full host where possible). */
  54. private String topicUser = "";
  55. /** Unixtimestamp representing time when the topic was set. */
  56. private long topicTime;
  57. /** Has this channel ever had a topic? */
  58. private boolean hadTopic;
  59. /** Known boolean-modes for channel. */
  60. private long modes;
  61. /** Reference to the parser object that owns this channel, Used for modes. */
  62. private final IRCParser parser; // Reference to parser object that owns this channel. Used for Modes
  63. /** Channel Name. */
  64. private final String name;
  65. /** Hashtable containing references to ChannelClients. */
  66. private final Map<String, IRCChannelClientInfo> clients = Collections.synchronizedMap(new HashMap<String, IRCChannelClientInfo>());
  67. /** Hashtable storing values for modes set in the channel that use parameters. */
  68. private final Map<Character, String> paramModes = new HashMap<>();
  69. /** Hashtable storing list modes. */
  70. private final Map<Character, ArrayList<ChannelListModeItem>> listModes = new HashMap<>();
  71. /**
  72. * LinkedList storing status of mode adding.
  73. * if an item is in this list for a mode, we are expecting new items for the list
  74. */
  75. private final List<Character> addingModes = new LinkedList<>();
  76. /** Modes waiting to be sent to the server. */
  77. private final List<String> modeQueue = new LinkedList<>();
  78. /** A Map to allow applications to attach misc data to this object. */
  79. private final Map<Object, Object> map;
  80. /** Queue of requested list modes. */
  81. private final Queue<Character> listModeQueue = new LinkedList<>();
  82. /** Listmode Queue Time. */
  83. private long listModeQueueTime = System.currentTimeMillis();
  84. /** Have we asked the server for the list modes for this channel yet? */
  85. private boolean askedForListModes;
  86. /** Has OnChannelGotListModes ever been called for this channel? */
  87. private boolean hasGotListModes;
  88. /**
  89. * Create a new channel object.
  90. *
  91. * @param parser Reference to parser that owns this channelclient (used for modes)
  92. * @param name Channel name.
  93. */
  94. public IRCChannelInfo(final IRCParser parser, final String name) {
  95. map = new HashMap<>();
  96. this.parser = parser;
  97. this.name = name;
  98. }
  99. /**
  100. * Get the listModeQueue.
  101. *
  102. * @return The listModeQueue
  103. */
  104. public Queue<Character> getListModeQueue() {
  105. Queue<Character> result = listModeQueue;
  106. final long now = System.currentTimeMillis();
  107. // Incase of breakage, if getListModeQueue() was last called greater than
  108. // 60 seconds ago, we reset the list.
  109. if (now - (30 * 1000) > listModeQueueTime) {
  110. result = new LinkedList<>();
  111. parser.callDebugInfo(IRCParser.DEBUG_LMQ, "Resetting LMQ");
  112. }
  113. listModeQueueTime = now;
  114. return result;
  115. }
  116. /**
  117. * Ask the server for all the list modes for this channel.
  118. */
  119. @Override
  120. public void requestListModes() {
  121. final IRCChannelClientInfo me = getChannelClient(parser.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 ServerType serverType = parser.getServerType();
  128. // We are considered opped if we have a mode higher than voice (or if we have any modes if voice doesn't exist)
  129. long voiceValue = 0;
  130. if (parser.prefixModes.get('v') != null) {
  131. voiceValue = parser.prefixModes.get('v');
  132. }
  133. final boolean isOpped = me.getImportantModeValue() > voiceValue;
  134. int modecount = 1;
  135. if (!ServerTypeGroup.SINGLE_LISTMODE.isMember(serverType) && parser.h005Info.containsKey("MODES")) {
  136. try {
  137. modecount = Integer.parseInt(parser.h005Info.get("MODES"));
  138. } catch (NumberFormatException e) {
  139. modecount = 1;
  140. }
  141. }
  142. // Support for potential future decent mode listing in the protocol
  143. //
  144. // See my proposal: http://shane.dmdirc.com/listmodes.php
  145. // Add listmode handler
  146. final boolean supportLISTMODE = parser.h005Info.containsKey("LISTMODE");
  147. String listmodes = "";
  148. int i = 0;
  149. for (Character cTemp : parser.chanModesOther.keySet()) {
  150. final int nTemp = parser.chanModesOther.get(cTemp);
  151. if (nTemp == IRCParser.MODE_LIST) {
  152. if (!isOpped && serverType.isOpOnly(cTemp)) {
  153. // IRCD doesn't allow non-ops to ask for these modes.
  154. continue;
  155. } else if (serverType == ServerType.STARCHAT && cTemp == 'H') {
  156. // IRCD Denies the mode exists
  157. continue;
  158. }
  159. i++;
  160. listmodes = listmodes + cTemp;
  161. if (i >= modecount && !supportLISTMODE) {
  162. parser.sendString("MODE " + getName() + " " + listmodes, QueuePriority.LOW);
  163. i = 0;
  164. listmodes = "";
  165. }
  166. }
  167. }
  168. if (i > 0) {
  169. if (supportLISTMODE) {
  170. parser.sendString("LISTMODE " + getName() + " " + listmodes, QueuePriority.LOW);
  171. } else {
  172. parser.sendString("MODE " + getName() + " " + listmodes, QueuePriority.LOW);
  173. }
  174. }
  175. }
  176. /**
  177. * Has this channel ever had a topic? (even an empty one!)
  178. *
  179. * @return True if a topic has ever been known for this channel.
  180. */
  181. public synchronized boolean hadTopic() {
  182. return hadTopic;
  183. }
  184. /**
  185. * Change the value of hadTopic to true.
  186. */
  187. public synchronized void setHadTopic() {
  188. this.hadTopic = true;
  189. }
  190. /**
  191. * Have we ever asked the server for this channels listmodes?
  192. *
  193. * @return True if requestListModes() has ever been used, else false
  194. */
  195. public boolean hasAskedForListModes() {
  196. return askedForListModes;
  197. }
  198. /**
  199. * Returns true if OnChannelGotListModes ever been called for this channel.
  200. *
  201. * @return True if OnChannelGotListModes ever been called for this channel.
  202. */
  203. public boolean hasGotListModes() {
  204. return hasGotListModes;
  205. }
  206. /**
  207. * Set if OnChannelGotListModes ever been called for this channel.
  208. *
  209. * @param newValue new value for if OnChannelGotListModes ever been called for this channel.
  210. */
  211. protected void setHasGotListModes(final boolean newValue) {
  212. hasGotListModes = newValue;
  213. }
  214. /** {@inheritDoc} */
  215. @Override
  216. public Map<Object, Object> getMap() {
  217. return map;
  218. }
  219. /**
  220. * Set if we are getting a names request or not.
  221. *
  222. * @param newValue if false, any new names reply will cause current known channelclients to be removed.
  223. */
  224. public void setAddingNames(final boolean newValue) {
  225. addingNames = newValue;
  226. }
  227. /**
  228. * Get if we are getting a names request or not.
  229. *
  230. * @return if false, any new names reply will cause current known channelclients to be removed.
  231. */
  232. public boolean isAddingNames() {
  233. return addingNames;
  234. }
  235. /** {@inheritDoc} */
  236. @Override
  237. public String getName() {
  238. return name;
  239. }
  240. /** {@inheritDoc} */
  241. @Override
  242. public int getChannelClientCount() {
  243. return clients.size();
  244. }
  245. /** {@inheritDoc} */
  246. @Override
  247. public Collection<ChannelClientInfo> getChannelClients() {
  248. synchronized (clients) {
  249. return new ArrayList<ChannelClientInfo>(clients.values());
  250. }
  251. }
  252. /**
  253. * Empty the channel (Remove all known channelclients).
  254. */
  255. protected void emptyChannel() {
  256. IRCClientInfo cTemp;
  257. synchronized (clients) {
  258. for (IRCChannelClientInfo client : clients.values()) {
  259. cTemp = client.getClient();
  260. cTemp.delChannelClientInfo(client);
  261. if (cTemp != parser.getLocalClient() && !cTemp.checkVisibility()) {
  262. parser.removeClient(cTemp);
  263. }
  264. }
  265. }
  266. clients.clear();
  267. }
  268. /** {@inheritDoc} */
  269. @Override
  270. public IRCChannelClientInfo getChannelClient(final String client) {
  271. return getChannelClient(client, false);
  272. }
  273. /** {@inheritDoc} */
  274. @Override
  275. public IRCChannelClientInfo getChannelClient(final String client, final boolean create) {
  276. final String who = parser.getStringConverter().toLowerCase(IRCClientInfo.parseHost(client));
  277. if (clients.containsKey(who)) {
  278. return clients.get(who);
  279. }
  280. if (create) {
  281. return new IRCChannelClientInfo(parser, new IRCClientInfo(parser, client).setFake(true), this);
  282. } else {
  283. return null;
  284. }
  285. }
  286. /** {@inheritDoc} */
  287. @Override
  288. public IRCChannelClientInfo getChannelClient(final ClientInfo client) {
  289. synchronized (clients) {
  290. for (IRCChannelClientInfo target : clients.values()) {
  291. if (target.getClient() == client) {
  292. return target;
  293. }
  294. }
  295. }
  296. return null;
  297. }
  298. /**
  299. * Get the ChannelClientInfo object associated with a ClientInfo object.
  300. *
  301. * @param cClient Client object to be added to channel
  302. * @return ChannelClientInfo object added, or an existing object if already known on channel
  303. */
  304. protected IRCChannelClientInfo addClient(final IRCClientInfo cClient) {
  305. IRCChannelClientInfo cTemp = getChannelClient(cClient);
  306. if (cTemp == null) {
  307. cTemp = new IRCChannelClientInfo(parser, cClient, this);
  308. clients.put(parser.getStringConverter().toLowerCase(cTemp.getClient().getNickname()), cTemp);
  309. }
  310. return cTemp;
  311. }
  312. /**
  313. * Remove ChannelClientInfo object associated with a ClientInfo object.
  314. *
  315. * @param cClient Client object to be removed from channel
  316. */
  317. protected void delClient(final IRCClientInfo cClient) {
  318. final IRCChannelClientInfo cTemp = getChannelClient(cClient);
  319. if (cTemp != null) {
  320. final IRCClientInfo clTemp = cTemp.getClient();
  321. clTemp.delChannelClientInfo(cTemp);
  322. if (clTemp != parser.getLocalClient() && !clTemp.checkVisibility()) {
  323. parser.removeClient(clTemp);
  324. }
  325. clients.remove(parser.getStringConverter().toLowerCase(cTemp.getClient().getNickname()));
  326. }
  327. }
  328. /**
  329. * Rename a channelClient.
  330. *
  331. * @param oldNickname Nickname client used to be known as
  332. * @param cChannelClient ChannelClient object with updated client object
  333. */
  334. protected void renameClient(final String oldNickname, final IRCChannelClientInfo cChannelClient) {
  335. if (clients.containsKey(oldNickname)) {
  336. final IRCChannelClientInfo cTemp = clients.get(oldNickname);
  337. if (cTemp == cChannelClient) {
  338. // Remove the old key
  339. clients.remove(oldNickname);
  340. // Add with the new key. (getNickname will return the new name not the
  341. // old one)
  342. clients.put(parser.getStringConverter().toLowerCase(cTemp.getClient().getNickname()), cTemp);
  343. }
  344. }
  345. }
  346. /**
  347. * Set the create time.
  348. *
  349. * @param nNewTime New unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
  350. */
  351. protected void setCreateTime(final long nNewTime) {
  352. creationTime = nNewTime;
  353. }
  354. /**
  355. * Get the Create time.
  356. *
  357. * @return Unixtimestamp time for the channel creation (Seconds since epoch, not milliseconds)
  358. */
  359. public long getCreateTime() {
  360. return creationTime;
  361. }
  362. /**
  363. * Set the topic time.
  364. *
  365. * @param nNewTime New unixtimestamp time for the topic (Seconds since epoch, not milliseconds)
  366. */
  367. protected void setTopicTime(final long nNewTime) {
  368. topicTime = nNewTime;
  369. }
  370. /** {@inheritDoc} */
  371. @Override
  372. public long getTopicTime() {
  373. return topicTime;
  374. }
  375. /**
  376. * Set the topic.
  377. *
  378. * @param sNewTopic New contents of topic
  379. */
  380. protected void setInternalTopic(final String sNewTopic) {
  381. topic = sNewTopic;
  382. }
  383. /** {@inheritDoc} */
  384. @Override
  385. public String getTopic() {
  386. return topic;
  387. }
  388. /**
  389. * Set the topic creator.
  390. *
  391. * @param sNewUser New user who set the topic (nickname if gotten on connect, full host if seen by parser)
  392. */
  393. protected void setTopicUser(final String sNewUser) {
  394. topicUser = sNewUser;
  395. }
  396. /** {@inheritDoc} */
  397. @Override
  398. public String getTopicSetter() {
  399. return topicUser;
  400. }
  401. /**
  402. * Set the channel modes (as an integer).
  403. *
  404. * @param nNewMode new long representing channel modes. (Boolean only)
  405. */
  406. protected void setMode(final long nNewMode) {
  407. modes = nNewMode;
  408. }
  409. /**
  410. * Get the channel modes (as an integer).
  411. *
  412. * @return long representing channel modes. (Boolean only)
  413. */
  414. public long getMode() {
  415. return modes;
  416. }
  417. /** {@inheritDoc} */
  418. @Override
  419. public String getModes() {
  420. final StringBuilder sModes = new StringBuilder("+");
  421. final StringBuilder sModeParams = new StringBuilder();
  422. String sTemp;
  423. long nTemp;
  424. final long nChanModes = this.getMode();
  425. for (char cTemp : parser.chanModesBool.keySet()) {
  426. nTemp = parser.chanModesBool.get(cTemp);
  427. if ((nChanModes & nTemp) == nTemp) {
  428. sModes.append(cTemp);
  429. }
  430. }
  431. for (char cTemp : paramModes.keySet()) {
  432. sTemp = paramModes.get(cTemp);
  433. if (!sTemp.isEmpty()) {
  434. sModes.append(cTemp);
  435. sModeParams.append(" ").append(this.getMode(cTemp));
  436. }
  437. }
  438. return sModes.append(sModeParams).toString();
  439. }
  440. /**
  441. * Set a channel mode that requires a parameter.
  442. *
  443. * @param cMode Character representing mode
  444. * @param sValue String repreenting value (if "" mode is unset)
  445. */
  446. protected void setModeParam(final Character cMode, final String sValue) {
  447. if (sValue.isEmpty()) {
  448. if (paramModes.containsKey(cMode)) {
  449. paramModes.remove(cMode);
  450. }
  451. } else {
  452. paramModes.put(cMode, sValue);
  453. }
  454. }
  455. /** {@inheritDoc} */
  456. @Override
  457. public String getMode(final char mode) {
  458. if (paramModes.containsKey(mode)) {
  459. return paramModes.get(mode);
  460. }
  461. return "";
  462. }
  463. /**
  464. * Add/Remove a value to a channel list.
  465. *
  466. * @param givenMode Character representing mode
  467. * @param givenItem ChannelListModeItem representing the item
  468. * @param bAdd Add or remove the value. (true for add, false for remove)
  469. */
  470. protected void setListModeParam(final Character givenMode, final ChannelListModeItem givenItem, final boolean bAdd) {
  471. Character cMode = givenMode;
  472. ChannelListModeItem newItem = givenItem;
  473. if (!parser.chanModesOther.containsKey(cMode) || parser.chanModesOther.get(cMode) != IRCParser.MODE_LIST) {
  474. return;
  475. }
  476. // Hyperion sucks.
  477. if (cMode == 'b' || cMode == 'q') {
  478. final ServerType serverType = parser.getServerType();
  479. if (ServerTypeGroup.FREENODE.isMember(serverType)) {
  480. if (cMode == 'b' && givenItem.getItem().charAt(0) == '%') {
  481. cMode = 'q';
  482. } else if (cMode == 'q' && givenItem.getItem().charAt(0) != '%') {
  483. cMode = 'b';
  484. }
  485. if (givenItem.getItem().charAt(0) == '%') {
  486. newItem = new ChannelListModeItem(givenItem.getItem().substring(1), givenItem.getOwner(), givenItem.getTime());
  487. }
  488. }
  489. }
  490. if (!listModes.containsKey(cMode)) {
  491. listModes.put(cMode, new ArrayList<ChannelListModeItem>());
  492. }
  493. final List<ChannelListModeItem> lModes = listModes.get(cMode);
  494. for (int i = 0; i < lModes.size(); i++) {
  495. if (parser.getStringConverter().equalsIgnoreCase(lModes.get(i).getItem(), newItem.getItem())) {
  496. if (bAdd) {
  497. return;
  498. } else {
  499. lModes.remove(i);
  500. break;
  501. }
  502. }
  503. }
  504. if (bAdd) {
  505. lModes.add(newItem);
  506. }
  507. }
  508. /** {@inheritDoc} */
  509. @Override
  510. public Collection<ChannelListModeItem> getListMode(final char mode) {
  511. if (!parser.chanModesOther.containsKey(mode) || parser.chanModesOther.get(mode) != IRCParser.MODE_LIST) {
  512. return null;
  513. }
  514. if (!listModes.containsKey(mode)) {
  515. listModes.put(mode, new ArrayList<ChannelListModeItem>());
  516. }
  517. return listModes.get(mode);
  518. }
  519. /**
  520. * Get the "adding state" of a list mode.
  521. *
  522. * @param cMode Character representing mode
  523. * @return false if we are not expecting a 367 etc, else true.
  524. */
  525. public boolean getAddState(final Character cMode) {
  526. synchronized (addingModes) {
  527. return addingModes.contains(cMode);
  528. }
  529. }
  530. /**
  531. * Get the "adding state" of a list mode.
  532. *
  533. * @param cMode Character representing mode
  534. * @param newState change the value returned by getAddState
  535. */
  536. protected void setAddState(final Character cMode, final boolean newState) {
  537. synchronized (addingModes) {
  538. if (newState) {
  539. addingModes.add(cMode);
  540. } else {
  541. if (addingModes.contains(cMode)) {
  542. addingModes.remove(cMode);
  543. }
  544. }
  545. }
  546. }
  547. /**
  548. * Reset the "adding state" of *all* list modes.
  549. */
  550. protected void resetAddState() {
  551. synchronized (addingModes) {
  552. addingModes.clear();
  553. }
  554. }
  555. /** {@inheritDoc} */
  556. @Override
  557. public void alterMode(final boolean add, final Character mode, final String parameter) {
  558. int modecount = 1;
  559. int modeint;
  560. String modestr;
  561. if (parser.h005Info.containsKey("MODES")) {
  562. try {
  563. modecount = Integer.parseInt(parser.h005Info.get("MODES"));
  564. } catch (NumberFormatException e) {
  565. if (parser.getServerType() == ServerType.OTHERNET) {
  566. modecount = 6;
  567. } else {
  568. modecount = 1;
  569. }
  570. }
  571. }
  572. if (!parser.isUserSettable(mode)) {
  573. return;
  574. }
  575. modestr = (add ? "+" : "-") + mode;
  576. if (parser.chanModesBool.containsKey(mode)) {
  577. final String teststr = (add ? "-" : "+") + mode;
  578. if (modeQueue.contains(teststr)) {
  579. modeQueue.remove(teststr);
  580. return;
  581. } else if (modeQueue.contains(modestr)) {
  582. return;
  583. }
  584. } else {
  585. // May need a param
  586. if (parser.prefixModes.containsKey(mode)) {
  587. modestr = modestr + " " + parameter;
  588. } else if (parser.chanModesOther.containsKey(mode)) {
  589. modeint = parser.chanModesOther.get(mode);
  590. if ((modeint & IRCParser.MODE_LIST) == IRCParser.MODE_LIST) {
  591. modestr = modestr + " " + parameter;
  592. } else if (!add && ((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
  593. modestr = modestr + " " + parameter;
  594. } else if (add && ((modeint & IRCParser.MODE_SET) == IRCParser.MODE_SET)) {
  595. // Does mode require a param to unset aswell?
  596. // We might need to queue an unset first
  597. if ((modeint & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET) {
  598. final String existingParam = getMode(mode);
  599. if (!existingParam.isEmpty()) {
  600. final String reverseModeStr = "-" + mode + " " + existingParam;
  601. parser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", reverseModeStr);
  602. modeQueue.add(reverseModeStr);
  603. if (modeQueue.size() == modecount) {
  604. flushModes();
  605. }
  606. }
  607. }
  608. modestr = modestr + " " + parameter;
  609. }
  610. } else {
  611. parser.callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Trying to alter unknown mode. positive: '" + add + "' | mode: '" + mode + "' | parameter: '" + parameter + "' ", ""));
  612. }
  613. }
  614. parser.callDebugInfo(IRCParser.DEBUG_INFO, "Queueing mode: %s", modestr);
  615. modeQueue.add(modestr);
  616. if (modeQueue.size() == modecount) {
  617. flushModes();
  618. }
  619. }
  620. /** {@inheritDoc} */
  621. @Override
  622. public void flushModes() {
  623. if (modeQueue.isEmpty()) {
  624. return;
  625. }
  626. final StringBuilder positivemode = new StringBuilder();
  627. final StringBuilder positiveparam = new StringBuilder();
  628. final StringBuilder negativemode = new StringBuilder();
  629. final StringBuilder negativeparam = new StringBuilder();
  630. final StringBuilder sendModeStr = new StringBuilder();
  631. String modestr;
  632. String[] modeparam;
  633. boolean positive;
  634. for (int i = 0; i < modeQueue.size(); ++i) {
  635. modeparam = modeQueue.get(i).split(" ");
  636. modestr = modeparam[0];
  637. positive = modestr.charAt(0) == '+';
  638. if (positive) {
  639. positivemode.append(modestr.charAt(1));
  640. if (modeparam.length > 1) {
  641. positiveparam.append(" ").append(modeparam[1]);
  642. }
  643. } else {
  644. negativemode.append(modestr.charAt(1));
  645. if (modeparam.length > 1) {
  646. negativeparam.append(" ").append(modeparam[1]);
  647. }
  648. }
  649. }
  650. if (negativemode.length() > 0) {
  651. sendModeStr.append("-").append(negativemode);
  652. }
  653. if (positivemode.length() > 0) {
  654. sendModeStr.append("+").append(positivemode);
  655. }
  656. if (negativeparam.length() > 0) {
  657. sendModeStr.append(negativeparam);
  658. }
  659. if (positiveparam.length() > 0) {
  660. sendModeStr.append(positiveparam);
  661. }
  662. parser.callDebugInfo(IRCParser.DEBUG_INFO, "Sending mode: %s", sendModeStr.toString());
  663. parser.sendRawMessage("MODE " + name + " " + sendModeStr.toString());
  664. clearModeQueue();
  665. }
  666. /**
  667. * This function will clear the mode queue (WITHOUT Sending).
  668. */
  669. public void clearModeQueue() {
  670. modeQueue.clear();
  671. }
  672. /** {@inheritDoc} */
  673. @Override
  674. public void sendMessage(final String message) {
  675. if (message.isEmpty()) {
  676. return;
  677. }
  678. parser.sendString("PRIVMSG " + name, message);
  679. }
  680. /**
  681. * Send a notice message to a target.
  682. *
  683. * @param sMessage Message to send
  684. */
  685. public void sendNotice(final String sMessage) {
  686. if (sMessage.isEmpty()) {
  687. return;
  688. }
  689. parser.sendString("NOTICE " + name, sMessage);
  690. }
  691. /** {@inheritDoc} */
  692. @Override
  693. public void sendAction(final String action) {
  694. if (action.isEmpty()) {
  695. return;
  696. }
  697. sendCTCP("ACTION", action);
  698. }
  699. /**
  700. * Send a CTCP to a target.
  701. *
  702. * @param sType Type of CTCP
  703. * @param sMessage Optional Additional Parameters
  704. */
  705. public void sendCTCP(final String sType, final String sMessage) {
  706. if (sType.isEmpty()) {
  707. return;
  708. }
  709. final char char1 = (char) 1;
  710. if (sMessage.isEmpty()) {
  711. sendMessage(char1 + sType.toUpperCase() + sMessage + char1);
  712. } else {
  713. sendMessage(char1 + sType.toUpperCase() + " " + sMessage + char1);
  714. }
  715. }
  716. /**
  717. * Send a CTCPReply to a target.
  718. *
  719. * @param sType Type of CTCP
  720. * @param sMessage Optional Additional Parameters
  721. */
  722. public void sendCTCPReply(final String sType, final String sMessage) {
  723. if (sType.isEmpty()) {
  724. return;
  725. }
  726. final char char1 = (char) 1;
  727. if (sMessage.isEmpty()) {
  728. sendNotice(char1 + sType.toUpperCase() + sMessage + char1);
  729. } else {
  730. sendNotice(char1 + sType.toUpperCase() + " " + sMessage + char1);
  731. }
  732. }
  733. /**
  734. * Get a string representation of the Channel.
  735. *
  736. * @return String representation of the Channel.
  737. */
  738. @Override
  739. public String toString() {
  740. return name;
  741. }
  742. /** {@inheritDoc} */
  743. @Override
  744. public Parser getParser() {
  745. return parser;
  746. }
  747. /** {@inheritDoc} */
  748. @Override
  749. public void part(final String reason) {
  750. parser.partChannel(name, reason);
  751. }
  752. /** {@inheritDoc} */
  753. @Override
  754. public void setTopic(final String topic) {
  755. parser.sendRawMessage("TOPIC " + name + " :" + topic);
  756. }
  757. /** {@inheritDoc} */
  758. @Override
  759. public void sendWho() {
  760. parser.sendRawMessage("WHO " + name);
  761. }
  762. }