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

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