Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

Server.java 35KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /*
  2. * Copyright (c) 2006-2007 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;
  23. import com.dmdirc.actions.ActionManager;
  24. import com.dmdirc.actions.CoreActionType;
  25. import com.dmdirc.commandparser.CommandManager;
  26. import com.dmdirc.identities.ConfigManager;
  27. import com.dmdirc.identities.ConfigSource;
  28. import com.dmdirc.logger.ErrorLevel;
  29. import com.dmdirc.logger.Logger;
  30. import com.dmdirc.parser.ChannelInfo;
  31. import com.dmdirc.parser.ClientInfo;
  32. import com.dmdirc.parser.IRCParser;
  33. import com.dmdirc.parser.MyInfo;
  34. import com.dmdirc.parser.ParserError;
  35. import com.dmdirc.parser.ServerInfo;
  36. import com.dmdirc.parser.callbacks.CallbackNotFound;
  37. import com.dmdirc.parser.callbacks.interfaces.IAwayState;
  38. import com.dmdirc.parser.callbacks.interfaces.IAwayStateOther;
  39. import com.dmdirc.parser.callbacks.interfaces.IChannelSelfJoin;
  40. import com.dmdirc.parser.callbacks.interfaces.IConnectError;
  41. import com.dmdirc.parser.callbacks.interfaces.IErrorInfo;
  42. import com.dmdirc.parser.callbacks.interfaces.IGotNetwork;
  43. import com.dmdirc.parser.callbacks.interfaces.IMOTDEnd;
  44. import com.dmdirc.parser.callbacks.interfaces.IMOTDLine;
  45. import com.dmdirc.parser.callbacks.interfaces.IMOTDStart;
  46. import com.dmdirc.parser.callbacks.interfaces.INickInUse;
  47. import com.dmdirc.parser.callbacks.interfaces.INoticeAuth;
  48. import com.dmdirc.parser.callbacks.interfaces.INumeric;
  49. import com.dmdirc.parser.callbacks.interfaces.IPingFailed;
  50. import com.dmdirc.parser.callbacks.interfaces.IPingSuccess;
  51. import com.dmdirc.parser.callbacks.interfaces.IPost005;
  52. import com.dmdirc.parser.callbacks.interfaces.IPrivateAction;
  53. import com.dmdirc.parser.callbacks.interfaces.IPrivateCTCP;
  54. import com.dmdirc.parser.callbacks.interfaces.IPrivateCTCPReply;
  55. import com.dmdirc.parser.callbacks.interfaces.IPrivateMessage;
  56. import com.dmdirc.parser.callbacks.interfaces.IPrivateNotice;
  57. import com.dmdirc.parser.callbacks.interfaces.ISocketClosed;
  58. import com.dmdirc.parser.callbacks.interfaces.IUserModeChanged;
  59. import com.dmdirc.ui.interfaces.InputWindow;
  60. import com.dmdirc.ui.MainFrame;
  61. import com.dmdirc.ui.ServerFrame;
  62. import com.dmdirc.ui.input.TabCompleter;
  63. import com.dmdirc.ui.messages.Formatter;
  64. import java.net.URL;
  65. import java.util.ArrayList;
  66. import java.util.Hashtable;
  67. import java.util.List;
  68. import java.util.Map;
  69. import java.util.Timer;
  70. import java.util.TimerTask;
  71. import javax.swing.ImageIcon;
  72. import javax.swing.JInternalFrame;
  73. import javax.swing.SwingUtilities;
  74. /**
  75. * The Server class represents the client's view of a server. It maintains
  76. * a list of all channels, queries, etc, and handles parser callbacks pertaining
  77. * to the server.
  78. *
  79. * @author chris
  80. */
  81. public final class Server extends FrameContainer implements IChannelSelfJoin,
  82. IPrivateMessage, IPrivateAction, IErrorInfo, IPrivateCTCP,
  83. IPrivateCTCPReply, ISocketClosed, IPrivateNotice, IMOTDStart,
  84. IMOTDLine, IMOTDEnd, INumeric, IGotNetwork, IPingFailed, IPingSuccess,
  85. IAwayState, IConnectError, IAwayStateOther, INickInUse, IPost005,
  86. INoticeAuth, IUserModeChanged {
  87. /** The callbacks that should be registered for server instances. */
  88. private static final String[] CALLBACKS = {
  89. "OnChannelSelfJoin", "OnErrorInfo", "OnPrivateMessage", "OnPingSuccess",
  90. "OnPrivateAction", "OnPrivateCTCP", "OnPrivateNotice", "OnConnectError",
  91. "OnPrivateCTCPReply", "OnSocketClosed", "OnGotNetwork", "OnNumeric",
  92. "OnMOTDStart", "OnMOTDLine", "OnMOTDEnd", "OnPingFailed", "OnAwayState",
  93. "OnAwayStateOther", "OnNickInUse", "OnPost005", "OnNoticeAuth",
  94. "OnUserModeChanged",
  95. };
  96. /** Open channels that currently exist on the server. */
  97. private final Map<String, Channel> channels = new Hashtable<String, Channel>();
  98. /** Open query windows on the server. */
  99. private final Map<String, Query> queries = new Hashtable<String, Query>();
  100. /** The IRC Parser instance handling this server. */
  101. private IRCParser parser;
  102. /** The raw frame used for this server instance. */
  103. private Raw raw;
  104. /** The ServerFrame corresponding to this server. */
  105. private ServerFrame frame;
  106. /** The name of the server we're connecting to. */
  107. private String server;
  108. /** The port we're connecting to. */
  109. private int port;
  110. /** The password we're using to connect. */
  111. private String password;
  112. /** Whether we're using SSL or not. */
  113. private boolean ssl;
  114. /** The profile we're using. */
  115. private ConfigSource profile;
  116. /**
  117. * Used to indicate that this server is in the process of closing all of its
  118. * windows, and thus requests for individual ones to be closed should be
  119. * ignored.
  120. */
  121. private boolean closing;
  122. /** The tabcompleter used for this server. */
  123. private final TabCompleter tabCompleter = new TabCompleter();
  124. /** The last activated internal frame for this server. */
  125. private FrameContainer activeFrame = this;
  126. /** The config manager for this server. */
  127. private ConfigManager configManager;
  128. /** Whether we're marked as away or not. */
  129. private boolean away;
  130. /** Our reason for being away, if any. */
  131. private String awayMessage;
  132. /** Whether we should attempt to reconnect or not. */
  133. private boolean reconnect = true;
  134. /**
  135. * Creates a new instance of Server.
  136. *
  137. * @param server The hostname/ip of the server to connect to
  138. * @param port The port to connect to
  139. * @param password The server password
  140. * @param ssl Whether to use SSL or not
  141. * @param profile The profile to use
  142. */
  143. public Server(final String server, final int port, final String password,
  144. final boolean ssl, final ConfigSource profile) {
  145. super();
  146. this.server = server;
  147. ServerManager.getServerManager().registerServer(this);
  148. configManager = new ConfigManager("", "", server);
  149. frame = new ServerFrame(this);
  150. frame.setTitle(server + ":" + port);
  151. frame.getInputHandler().setTabCompleter(tabCompleter);
  152. frame.addInternalFrameListener(this);
  153. MainFrame.getMainFrame().addChild(frame);
  154. frame.open();
  155. tabCompleter.addEntries(CommandManager.getServerCommandNames());
  156. tabCompleter.addEntries(CommandManager.getGlobalCommandNames());
  157. new Timer().scheduleAtFixedRate(new TimerTask() {
  158. public void run() {
  159. for (Channel channel : channels.values()) {
  160. channel.checkWho();
  161. }
  162. }
  163. }, 0, Config.getOptionInt("general", "whotime", 60000));
  164. connect(server, port, password, ssl, profile);
  165. }
  166. /**
  167. * Connects to a new server with the specified details.
  168. *
  169. * @param server The hostname/ip of the server to connect to
  170. * @param port The port to connect to
  171. * @param password The server password
  172. * @param ssl Whether to use SSL or not
  173. * @param profile The profile to use
  174. */
  175. public void connect(final String server, final int port, final String password,
  176. final boolean ssl, final ConfigSource profile) {
  177. if (closing) {
  178. Logger.error(ErrorLevel.WARNING, "Attempted to connect to a server while frame is closing.");
  179. return;
  180. }
  181. reconnect = true;
  182. if (parser != null && parser.getSocketState() == parser.STATE_OPEN) {
  183. disconnect(configManager.getOption("general", "quitmessage"));
  184. }
  185. this.server = server;
  186. this.port = port;
  187. this.password = password;
  188. this.ssl = ssl;
  189. this.profile = profile;
  190. configManager = new ConfigManager("", "", server);
  191. final ClassLoader cldr = this.getClass().getClassLoader();
  192. URL imageURL;
  193. if (ssl) {
  194. imageURL = cldr.getResource("com/dmdirc/res/secure-server.png");
  195. } else {
  196. imageURL = cldr.getResource("com/dmdirc/res/server.png");
  197. }
  198. imageIcon = new ImageIcon(imageURL);
  199. frame.setFrameIcon(imageIcon);
  200. addLine("serverConnecting", server, port);
  201. final MyInfo myInfo = new MyInfo();
  202. myInfo.setNickname(profile.getOption("profile", "nickname"));
  203. myInfo.setRealname(profile.getOption("profile", "realname"));
  204. if (profile.hasOption("profile", "ident")) {
  205. myInfo.setUsername(profile.getOption("profile", "ident"));
  206. }
  207. final ServerInfo serverInfo = new ServerInfo(server, port, password);
  208. serverInfo.setSSL(ssl);
  209. parser = new IRCParser(myInfo, serverInfo).setCreateFake(true);
  210. if (raw == null && Config.getOptionBool("general", "showrawwindow")) {
  211. raw = new Raw(this);
  212. MainFrame.getMainFrame().getFrameManager().addCustom(this, raw);
  213. }
  214. if (raw != null) {
  215. raw.registerCallbacks();
  216. }
  217. try {
  218. for (String callback : CALLBACKS) {
  219. parser.getCallbackManager().addCallback(callback, this);
  220. }
  221. } catch (CallbackNotFound ex) {
  222. Logger.error(ErrorLevel.FATAL, "Unable to register server event handlers", ex);
  223. }
  224. for (Query query : queries.values()) {
  225. query.reregister();
  226. }
  227. away = false;
  228. awayMessage = null;
  229. frame.setAway(false);
  230. try {
  231. new Thread(parser).start();
  232. } catch (IllegalThreadStateException ex) {
  233. Logger.error(ErrorLevel.FATAL, "Unable to start IRC Parser", ex);
  234. }
  235. updateIgnoreList();
  236. }
  237. /**
  238. * Reconnects to the IRC server with a specified reason.
  239. *
  240. * @param reason The quit reason to send
  241. */
  242. public void reconnect(final String reason) {
  243. disconnect(reason);
  244. connect(server, port, password, ssl, profile);
  245. }
  246. /**
  247. * Reconnects to the IRC server.
  248. */
  249. public void reconnect() {
  250. reconnect(Config.getOption("general", "reconnectmessage"));
  251. }
  252. /**
  253. * Updates the ignore list for this server.
  254. */
  255. public void updateIgnoreList() {
  256. if (parser == null || parser.getIgnoreList() == null) {
  257. return;
  258. }
  259. parser.getIgnoreList().clear();
  260. if (configManager.hasOption("network", "ignorelist")) {
  261. for (String line : configManager.getOption("network", "ignorelist").split("\n")) {
  262. parser.getIgnoreList().add(line);
  263. }
  264. }
  265. }
  266. /**
  267. * Determines whether the server knows of the specified channel.
  268. *
  269. * @param channel The channel to be checked
  270. * @return True iff the channel is known, false otherwise
  271. */
  272. public boolean hasChannel(final String channel) {
  273. return channels.containsKey(parser.toLowerCase(channel));
  274. }
  275. /**
  276. * Retrieves the specified channel belonging to this server.
  277. *
  278. * @param channel The channel to be retrieved
  279. * @return The appropriate channel object
  280. */
  281. public Channel getChannel(final String channel) {
  282. return channels.get(parser.toLowerCase(channel));
  283. }
  284. /**
  285. * Retrieves a list of channel names belonging to this server.
  286. *
  287. * @return list of channel names belonging to this server
  288. */
  289. public List<String> getChannels() {
  290. final ArrayList<String> res = new ArrayList<String>();
  291. for (String channel : channels.keySet()) {
  292. res.add(channel);
  293. }
  294. return res;
  295. }
  296. /**
  297. * Determines whether the server knows of the specified query.
  298. *
  299. * @param query The query to be checked
  300. * @return True iff the query is known, false otherwise
  301. */
  302. public boolean hasQuery(final String query) {
  303. return queries.containsKey(parser.toLowerCase(query));
  304. }
  305. /**
  306. * Retrieves the specified query belonging to this server.
  307. *
  308. * @param query The query to be retrieved
  309. * @return The appropriate query object
  310. */
  311. public Query getQuery(final String query) {
  312. return queries.get(parser.toLowerCase(query));
  313. }
  314. /**
  315. * Retrieves a list of queries belonging to this server.
  316. *
  317. * @return list of queries belonging to this server
  318. */
  319. public List<String> getQueries() {
  320. final ArrayList<String> res = new ArrayList<String>();
  321. for (String query : queries.keySet()) {
  322. res.add(query);
  323. }
  324. return res;
  325. }
  326. /**
  327. * Retrieves the raw window associated with this server.
  328. *
  329. * @return The raw window associated with this server.
  330. */
  331. public Raw getRaw() {
  332. return raw;
  333. }
  334. /**
  335. * Retrieves the parser used for this connection.
  336. *
  337. * @return IRCParser this connection's parser
  338. */
  339. public IRCParser getParser() {
  340. return parser;
  341. }
  342. /**
  343. * Retrieves the profile that's in use for this server.
  344. *
  345. * @return The profile in use by this server
  346. */
  347. public ConfigSource getProfile() {
  348. return profile;
  349. }
  350. /**
  351. * Retrieves the name of this server.
  352. *
  353. * @return The name of this server
  354. */
  355. public String getName() {
  356. return this.server;
  357. }
  358. /**
  359. * Retrieves the name of this server's network.
  360. *
  361. * @return The name of this server's network
  362. */
  363. public String getNetwork() {
  364. return parser.getNetworkName();
  365. }
  366. /**
  367. * Retrieves the name of this server's IRCd.
  368. *
  369. * @return The name of this server's IRCd
  370. */
  371. public String getIrcd() {
  372. return parser.getIRCD(true);
  373. }
  374. /**
  375. * Returns the current away status.
  376. *
  377. * @return True if the client is marked as away, false otherwise
  378. */
  379. public boolean isAway() {
  380. return away;
  381. }
  382. /**
  383. * Gets the current away message.
  384. *
  385. * @return Null if the client isn't away, or a textual away message if it is
  386. */
  387. public String getAwayMessage() {
  388. return awayMessage;
  389. }
  390. /**
  391. * Returns the tab completer for this connection.
  392. *
  393. * @return The tab completer for this server
  394. */
  395. public TabCompleter getTabCompleter() {
  396. return tabCompleter;
  397. }
  398. /** {@inheritDoc} */
  399. public InputWindow getFrame() {
  400. return frame;
  401. }
  402. /** {@inheritDoc} */
  403. @Override
  404. public ConfigManager getConfigManager() {
  405. return configManager;
  406. }
  407. /**
  408. * Closes this server connection and associated windows.
  409. *
  410. * @param reason reason for closing
  411. */
  412. public void close(final String reason) {
  413. closing = true;
  414. if (parser != null) {
  415. // Unregister parser callbacks
  416. parser.getCallbackManager().delAllCallback(this);
  417. }
  418. // Unregister frame callbacks
  419. frame.removeInternalFrameListener(this);
  420. // Disconnect from the server
  421. disconnect(reason);
  422. // Close all channel windows
  423. closeChannels();
  424. // Close all query windows
  425. closeQueries();
  426. // Close the raw window
  427. if (raw != null) {
  428. raw.close();
  429. }
  430. // Unregister ourselves with the server manager
  431. ServerManager.getServerManager().unregisterServer(this);
  432. SwingUtilities.invokeLater(new Runnable() {
  433. public void run() {
  434. // Close our own window
  435. frame.setVisible(false);
  436. MainFrame.getMainFrame().delChild(frame);
  437. frame = null;
  438. }
  439. });
  440. // Ditch the parser
  441. parser = null;
  442. }
  443. /** {@inheritDoc} */
  444. public void close() {
  445. close(configManager.getOption("general", "quitmessage"));
  446. }
  447. /**
  448. * Disconnects from the server.
  449. *
  450. * @param reason disconnect reason
  451. */
  452. public void disconnect(final String reason) {
  453. reconnect = false;
  454. if (parser != null && parser.isReady()) {
  455. parser.disconnect(reason);
  456. if (configManager.getOptionBool("general", "closechannelsonquit")) {
  457. closeChannels();
  458. } else {
  459. clearChannels();
  460. }
  461. if (configManager.getOptionBool("general", "closequeriesonquit")) {
  462. closeQueries();
  463. }
  464. }
  465. }
  466. /**
  467. * Closes all open channel windows associated with this server.
  468. */
  469. private void closeChannels() {
  470. final boolean wasClosing = closing;
  471. closing = true;
  472. for (Channel channel : channels.values()) {
  473. channel.closeWindow();
  474. }
  475. channels.clear();
  476. closing = wasClosing;
  477. }
  478. /**
  479. * Clears the nicklist of all open channels.
  480. */
  481. private void clearChannels() {
  482. for (Channel channel : channels.values()) {
  483. channel.resetWindow();
  484. }
  485. }
  486. /**
  487. * Closes all open query windows associated with this server.
  488. */
  489. private void closeQueries() {
  490. final boolean wasClosing = closing;
  491. closing = true;
  492. for (Query query : queries.values()) {
  493. query.close();
  494. }
  495. queries.clear();
  496. closing = wasClosing;
  497. }
  498. /**
  499. * Removes our reference to the raw object (presumably after it has been
  500. * closed).
  501. */
  502. public void delRaw() {
  503. MainFrame.getMainFrame().getFrameManager().delCustom(this, raw);
  504. raw = null;
  505. }
  506. /**
  507. * Removes a specific channel and window from this server.
  508. *
  509. * @param chan channel to remove
  510. */
  511. public void delChannel(final String chan) {
  512. tabCompleter.removeEntry(chan);
  513. MainFrame.getMainFrame().getFrameManager().delChannel(
  514. this, channels.get(parser.toLowerCase(chan)));
  515. if (!closing) {
  516. channels.remove(parser.toLowerCase(chan));
  517. }
  518. }
  519. /**
  520. * Adds a specific channel and window to this server.
  521. *
  522. * @param chan channel to add
  523. */
  524. private void addChannel(final ChannelInfo chan) {
  525. final Channel newChan = new Channel(this, chan);
  526. tabCompleter.addEntry(chan.getName());
  527. channels.put(parser.toLowerCase(chan.getName()), newChan);
  528. MainFrame.getMainFrame().getFrameManager().addChannel(this, newChan);
  529. SwingUtilities.invokeLater(new Runnable() {
  530. public void run() {
  531. newChan.show();
  532. }
  533. });
  534. }
  535. /**
  536. * Adds a query query to this server.
  537. *
  538. * @param host host of the remote client being queried
  539. */
  540. public void addQuery(final String host) {
  541. final Query newQuery = new Query(this, host);
  542. tabCompleter.addEntry(ClientInfo.parseHost(host));
  543. queries.put(parser.toLowerCase(ClientInfo.parseHost(host)), newQuery);
  544. MainFrame.getMainFrame().getFrameManager().addQuery(this, newQuery);
  545. }
  546. /**
  547. * Deletes a query from this server.
  548. *
  549. * @param host host of the remote client being queried
  550. */
  551. public void delQuery(final String host) {
  552. tabCompleter.removeEntry(ClientInfo.parseHost(host));
  553. MainFrame.getMainFrame().getFrameManager().delQuery(this,
  554. queries.get(parser.toLowerCase(ClientInfo.parseHost(host))));
  555. if (!closing) {
  556. queries.remove(parser.toLowerCase(ClientInfo.parseHost(host)));
  557. }
  558. }
  559. /** {@inheritDoc} */
  560. @Override
  561. public boolean ownsFrame(final JInternalFrame target) {
  562. // Check if it's our server frame
  563. if (frame != null && frame.equals(target)) { return true; }
  564. // Check if it's the raw frame
  565. if (raw != null && raw.ownsFrame(target)) { return true; }
  566. // Check if it's a channel frame
  567. for (Channel channel : channels.values()) {
  568. if (channel.ownsFrame(target)) { return true; }
  569. }
  570. // Check if it's a query frame
  571. for (Query query : queries.values()) {
  572. if (query.ownsFrame(target)) { return true; }
  573. }
  574. return false;
  575. }
  576. /**
  577. * Sets the specified frame as the most-recently activated.
  578. *
  579. * @param source The frame that was activated
  580. */
  581. public void setActiveFrame(final FrameContainer source) {
  582. activeFrame = source;
  583. }
  584. /**
  585. * Passes the arguments to the most recently activated frame for this
  586. * server. If the frame isn't know, or isn't visible, use this frame
  587. * instead.
  588. *
  589. * @param messageType The type of message to send
  590. * @param args The arguments for the message
  591. */
  592. public void addLineToActive(final String messageType, final Object... args) {
  593. if (activeFrame == null || !((JInternalFrame) activeFrame.getFrame()).isVisible()) {
  594. activeFrame = this;
  595. }
  596. activeFrame.getFrame().addLine(messageType, args);
  597. }
  598. /**
  599. * Passes the arguments to all frames for this server.
  600. *
  601. * @param messageType The type of message to send
  602. * @param args The arguments of the message
  603. */
  604. public void addLineToAll(final String messageType, final Object... args) {
  605. for (Channel channel : channels.values()) {
  606. channel.addLine(messageType, args);
  607. }
  608. for (Query query : queries.values()) {
  609. query.addLine(messageType, args);
  610. }
  611. addLine(messageType, args);
  612. }
  613. /**
  614. * Handles general server notifications (i.e., ones note tied to a
  615. * specific window). The user can select where the notifications should
  616. * go in their config.
  617. *
  618. * @param messageType The type of message that is being sent
  619. * @param args The arguments for the message
  620. */
  621. public void handleNotification(final String messageType, final Object... args) {
  622. String target = "server";
  623. if (configManager.hasOption("notifications", messageType)) {
  624. final String newTarget = configManager.getOption("notifications", messageType);
  625. if ("server".equals(newTarget) || "all".equals(newTarget) || "active".equals(newTarget)) {
  626. target = newTarget;
  627. }
  628. }
  629. if ("server".equals(target)) {
  630. addLine(messageType, args);
  631. } else if ("all".equals(target)) {
  632. addLineToAll(messageType, args);
  633. } else if ("active".equals(target)) {
  634. addLineToActive(messageType, args);
  635. }
  636. }
  637. /** {@inheritDoc} */
  638. public void onChannelSelfJoin(final IRCParser tParser, final ChannelInfo cChannel) {
  639. if (hasChannel(cChannel.getName())) {
  640. getChannel(cChannel.getName()).setChannelInfo(cChannel);
  641. getChannel(cChannel.getName()).selfJoin();
  642. } else {
  643. addChannel(cChannel);
  644. }
  645. }
  646. /** {@inheritDoc} */
  647. public void onPrivateMessage(final IRCParser tParser, final String sMessage,
  648. final String sHost) {
  649. if (!queries.containsKey(parser.toLowerCase(ClientInfo.parseHost(sHost)))) {
  650. addQuery(sHost);
  651. }
  652. }
  653. /** {@inheritDoc} */
  654. public void onPrivateAction(final IRCParser tParser, final String sMessage,
  655. final String sHost) {
  656. if (!queries.containsKey(parser.toLowerCase(ClientInfo.parseHost(sHost)))) {
  657. addQuery(sHost);
  658. }
  659. }
  660. /** {@inheritDoc} */
  661. public void onPrivateCTCP(final IRCParser tParser, final String sType,
  662. final String sMessage, final String sHost) {
  663. final String[] parts = ClientInfo.parseHostFull(sHost);
  664. handleNotification("privateCTCP", parts[0], parts[1], parts[2], sType, sMessage);
  665. sendCTCPReply(parts[0], sType, sMessage);
  666. }
  667. /**
  668. * Replies to an incoming CTCP message.
  669. *
  670. * @param source The source of the message
  671. * @param type The CTCP type
  672. * @param args The CTCP arguments
  673. */
  674. public void sendCTCPReply(final String source, final String type, final String args) {
  675. if (type.equalsIgnoreCase("VERSION")) {
  676. parser.sendCTCPReply(source, "VERSION", "DMDirc " + Main.VERSION
  677. + " - http://www.dmdirc.com/");
  678. } else if (type.equalsIgnoreCase("PING")) {
  679. parser.sendCTCPReply(source, "PING", args);
  680. } else if (type.equalsIgnoreCase("CLIENTINFO")) {
  681. parser.sendCTCPReply(source, "CLIENTINFO", "VERSION PING CLIENTINFO");
  682. }
  683. }
  684. /** {@inheritDoc} */
  685. public void onPrivateCTCPReply(final IRCParser tParser, final String sType,
  686. final String sMessage, final String sHost) {
  687. final String[] parts = ClientInfo.parseHostFull(sHost);
  688. handleNotification("privateCTCPreply", parts[0], parts[1], parts[2], sType, sMessage);
  689. }
  690. /** {@inheritDoc} */
  691. public void onPrivateNotice(final IRCParser tParser, final String sMessage,
  692. final String sHost) {
  693. final String[] parts = ClientInfo.parseHostFull(sHost);
  694. handleNotification("privateNotice", parts[0], parts[1], parts[2], sMessage);
  695. }
  696. /** {@inheritDoc} */
  697. public void onNickInUse(final IRCParser tParser, final String nickname) {
  698. final String lastNick = tParser.getMyNickname();
  699. // If our last nick is still valid, ignore the in use message
  700. if (!parser.equalsIgnoreCase(lastNick, nickname)) {
  701. return;
  702. }
  703. String newNick = null;
  704. if (profile.hasOption("profile", "altnicks")) {
  705. final String[] alts = profile.getOption("profile", "altnicks").split("\n");
  706. int offset = -1;
  707. if (!parser.equalsIgnoreCase(lastNick, profile.getOption("profile", "nickname"))) {
  708. for (String alt : alts) {
  709. offset++;
  710. if (parser.equalsIgnoreCase(alt, lastNick)) {
  711. break;
  712. }
  713. }
  714. }
  715. if (offset + 1 < alts.length) {
  716. newNick = alts[offset + 1];
  717. }
  718. }
  719. if (newNick == null) {
  720. newNick = lastNick + (int) (Math.random() * 10);
  721. }
  722. parser.setNickname(newNick);
  723. }
  724. /** {@inheritDoc} */
  725. public void onGotNetwork(final IRCParser tParser, final String networkName,
  726. final String ircdVersion, final String ircdType) {
  727. configManager = new ConfigManager(ircdType, networkName, this.server);
  728. updateIgnoreList();
  729. }
  730. /** {@inheritDoc} */
  731. public void onMOTDStart(final IRCParser tParser, final String sData) {
  732. addLine("motdStart", sData);
  733. }
  734. /** {@inheritDoc} */
  735. public void onMOTDLine(final IRCParser tParser, final String sData) {
  736. addLine("motdLine", sData);
  737. }
  738. /** {@inheritDoc} */
  739. public void onMOTDEnd(final IRCParser tParser, final boolean noMOTD) {
  740. addLine("motdEnd", "End of server's MOTD.");
  741. }
  742. /** {@inheritDoc} */
  743. public void onNumeric(final IRCParser tParser, final int numeric,
  744. final String[] token) {
  745. final String withIrcd = "numeric_" + tParser.getIRCD(true) + "_" + numeric;
  746. final String sansIrcd = "numeric_" + numeric;
  747. String target = null;
  748. if (Formatter.hasFormat(withIrcd)) {
  749. target = withIrcd;
  750. } else if (Formatter.hasFormat(sansIrcd)) {
  751. target = sansIrcd;
  752. } else if (Formatter.hasFormat("numeric_unknown")) {
  753. target = "numeric_unknown";
  754. }
  755. if (target != null) {
  756. handleNotification(target, (Object[]) token);
  757. }
  758. ActionManager.processEvent(CoreActionType.SERVER_NUMERIC, null, this,
  759. Integer.valueOf(numeric), token);
  760. }
  761. /** {@inheritDoc} */
  762. public void onNoticeAuth(final IRCParser tParser, final String sData) {
  763. handleNotification("authNotice", sData);
  764. ActionManager.processEvent(CoreActionType.SERVER_AUTHNOTICE, null, this,
  765. sData);
  766. }
  767. /** {@inheritDoc} */
  768. public void onUserModeChanged(final IRCParser tParser, final ClientInfo cClient,
  769. final String sSetBy, final String sModes) {
  770. if (!cClient.equals(parser.getMyself())) {
  771. return;
  772. }
  773. final ClientInfo setter = parser.getClientInfoOrFake(sSetBy);
  774. final String[] setterParts = ClientInfo.parseHostFull(sSetBy);
  775. final StringBuffer format = new StringBuffer("userModeChanged");
  776. ActionManager.processEvent(CoreActionType.SERVER_USERMODES, format,
  777. this, setter, sModes);
  778. frame.addLine(format, setterParts[0], setterParts[1], setterParts[2],
  779. sModes);
  780. }
  781. /** {@inheritDoc} */
  782. public void onAwayState(final IRCParser tParser, final boolean currentState,
  783. final String reason) {
  784. if (currentState) {
  785. away = true;
  786. awayMessage = reason;
  787. ActionManager.processEvent(CoreActionType.SERVER_AWAY, null, this, awayMessage);
  788. } else {
  789. away = false;
  790. awayMessage = null;
  791. ActionManager.processEvent(CoreActionType.SERVER_BACK, null, this);
  792. }
  793. frame.setAway(away);
  794. }
  795. /** {@inheritDoc} */
  796. public void onSocketClosed(final IRCParser tParser) {
  797. if (!reconnect) {
  798. // This has been triggered via .discconect()
  799. return;
  800. }
  801. handleNotification("socketClosed", this.server);
  802. if (configManager.getOptionBool("general", "closechannelsondisconnect")) {
  803. closeChannels();
  804. }
  805. if (configManager.getOptionBool("general", "closequeriesondisconnect")) {
  806. closeQueries();
  807. }
  808. if (reconnect && Config.getOptionBool("general", "reconnectondisconnect")) {
  809. final int delay = Config.getOptionInt("general", "reconnectdelay", 5);
  810. handleNotification("connectRetry", server, delay);
  811. new Timer().schedule(new TimerTask() {
  812. public void run() {
  813. reconnect();
  814. }
  815. }, delay * 1000);
  816. }
  817. }
  818. /** {@inheritDoc} */
  819. public void onConnectError(final IRCParser tParser, final ParserError errorInfo) {
  820. String description = "";
  821. if (errorInfo.getException() == null) {
  822. description = errorInfo.getData();
  823. } else {
  824. final Exception ex = errorInfo.getException();
  825. if (ex instanceof java.net.UnknownHostException) {
  826. description = "Unknown host (unable to resolve)";
  827. } else if (ex instanceof java.net.NoRouteToHostException) {
  828. description = "No route to host";
  829. } else if (ex instanceof java.net.SocketException) {
  830. description = ex.getMessage();
  831. } else {
  832. Logger.error(ErrorLevel.TRIVIAL, "Unknown socket error", ex);
  833. description = "Unknown error: " + ex.getMessage();
  834. }
  835. }
  836. handleNotification("connectError", server, description);
  837. if (Config.getOptionBool("general", "reconnectonconnectfailure")) {
  838. final int delay = Config.getOptionInt("general", "reconnectdelay", 5);
  839. handleNotification("connectRetry", server, delay);
  840. new Timer().schedule(new TimerTask() {
  841. public void run() {
  842. reconnect();
  843. }
  844. }, delay * 1000);
  845. }
  846. }
  847. /** {@inheritDoc} */
  848. public void onPingFailed(final IRCParser tParser) {
  849. MainFrame.getMainFrame().getStatusBar().setMessage("No ping reply from "
  850. + this.server + " for over "
  851. + Math.floor(parser.getPingTime(false) / 1000.0) + " seconds.", null, 10);
  852. ActionManager.processEvent(CoreActionType.SERVER_NOPING, null, this,
  853. Long.valueOf(parser.getPingTime(false)));
  854. if (parser.getPingTime(false) >= configManager.getOptionInt("server", "pingtimeout", 60000)) {
  855. handleNotification("stonedServer", server);
  856. reconnect();
  857. }
  858. }
  859. /** {@inheritDoc} */
  860. public void onPingSuccess(final IRCParser tParser) {
  861. ActionManager.processEvent(CoreActionType.SERVER_GOTPING, null, this,
  862. Long.valueOf(parser.getServerLag()));
  863. }
  864. /** {@inheritDoc} */
  865. public void onAwayStateOther(final IRCParser tParser,
  866. final ClientInfo client, final boolean state) {
  867. for (Channel chan : channels.values()) {
  868. chan.onAwayStateOther(tParser, client, state);
  869. }
  870. }
  871. /** {@inheritDoc} */
  872. public void onPost005(final IRCParser tParser) {
  873. ActionManager.processEvent(CoreActionType.SERVER_CONNECTED, null, this);
  874. if (configManager.hasOption("general", "rejoinchannels")) {
  875. for (Channel chan : channels.values()) {
  876. chan.join();
  877. }
  878. }
  879. }
  880. /** {@inheritDoc} */
  881. public void onErrorInfo(final IRCParser tParser, final ParserError errorInfo) {
  882. ErrorLevel errorLevel;
  883. if (errorInfo.isFatal()) {
  884. errorLevel = ErrorLevel.FATAL;
  885. } else if (errorInfo.isError()) {
  886. errorLevel = ErrorLevel.ERROR;
  887. } else if (errorInfo.isWarning()) {
  888. errorLevel = ErrorLevel.WARNING;
  889. } else {
  890. Logger.error(ErrorLevel.WARNING,
  891. "Unknown error level for parser error: " + errorInfo.getData());
  892. return;
  893. }
  894. if (errorInfo.isException()) {
  895. Logger.error(errorLevel, errorInfo.getData(), errorInfo.getException());
  896. } else {
  897. Logger.error(errorLevel, errorInfo.getData());
  898. }
  899. }
  900. /**
  901. * Returns this server's name.
  902. *
  903. * @return A string representation of this server (i.e., its name)
  904. */
  905. public String toString() {
  906. return this.server;
  907. }
  908. /**
  909. * Returns the server instance associated with this frame.
  910. *
  911. * @return the associated server connection
  912. */
  913. public Server getServer() {
  914. return this;
  915. }
  916. }