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

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