Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

IRCChannelInfo.java 27KB

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