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.

IRCParser.java 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. /*
  2. * Copyright (c) 2006-2007 Chris Smith, Shane Mc Cormack
  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. * SVN: $Id$
  23. */
  24. package uk.org.ownage.dmdirc.parser;
  25. import uk.org.ownage.dmdirc.parser.callbacks.CallbackManager;
  26. import uk.org.ownage.dmdirc.parser.callbacks.CallbackOnDataIn;
  27. import uk.org.ownage.dmdirc.parser.callbacks.CallbackOnDataOut;
  28. import uk.org.ownage.dmdirc.parser.callbacks.CallbackOnDebugInfo;
  29. import uk.org.ownage.dmdirc.parser.callbacks.CallbackOnErrorInfo;
  30. import uk.org.ownage.dmdirc.parser.callbacks.CallbackOnSocketClosed;
  31. import uk.org.ownage.dmdirc.parser.callbacks.interfaces.IDataIn;
  32. import uk.org.ownage.dmdirc.parser.callbacks.interfaces.IDataOut;
  33. import uk.org.ownage.dmdirc.parser.callbacks.interfaces.IDebugInfo;
  34. import uk.org.ownage.dmdirc.parser.callbacks.interfaces.IErrorInfo;
  35. import uk.org.ownage.dmdirc.parser.callbacks.interfaces.ISocketClosed;
  36. import java.io.BufferedReader;
  37. import java.io.InputStreamReader;
  38. import java.io.IOException;
  39. import java.io.PrintWriter;
  40. import java.net.Socket;
  41. import java.net.UnknownHostException;
  42. import java.util.Arrays;
  43. import java.util.Enumeration;
  44. import java.util.Hashtable;
  45. import javax.net.SocketFactory;
  46. import javax.net.ssl.SSLContext;
  47. import javax.net.ssl.SSLSocket;
  48. import javax.net.ssl.X509TrustManager;
  49. import javax.net.ssl.TrustManager;
  50. /**
  51. * IRC Parser.
  52. *
  53. * @author Shane Mc Cormack
  54. * @version $Id$
  55. */
  56. public class IRCParser implements Runnable {
  57. /** General Debug Information. */
  58. public static final int ndInfo = 1;
  59. /** Socket Debug Information. */
  60. public static final int ndSocket = 2;
  61. /** Processing Manager Debug Information. */
  62. public static final int ndProcessor = 4;
  63. // public static final int ndSomething = 8; //Next thingy
  64. /** Socket is not created yet. */
  65. public static final byte stateNull = 0;
  66. /** Socket is closed. */
  67. public static final byte stateClosed = 1;
  68. /** Socket is Open. */
  69. public static final byte stateOpen = 2;
  70. /** Attempt to update user host all the time, not just on Who/Add/NickChange. */
  71. protected static final boolean alwaysUpdateClient = true;
  72. /** Current Socket State */
  73. private byte nSocketState = 0;
  74. /**
  75. * Get the current socket State.
  76. *
  77. * @return Current SocketState (stateNull, stateClosed or stateOpen)
  78. */
  79. public byte getSocketState() { return nSocketState; }
  80. /** This is the socket used for reading from/writing to the IRC server. */
  81. private Socket socket = null;
  82. /** This is the socket used for reading from/writing to the IRC server when using SSL. */
  83. private SSLSocket sslSocket = null;
  84. /** Used for writing to the server. */
  85. private PrintWriter out = null;
  86. /** Used for reading from the server. */
  87. private BufferedReader in = null;
  88. /**
  89. * This is what the user wants settings to be.
  90. * Nickname here is *not* always accurate.<br><br>
  91. * ClientInfo variable tParser.getMyself() should be used for accurate info.
  92. */
  93. public MyInfo me = new MyInfo();
  94. /** Server Info requested by user. */
  95. public ServerInfo server = new ServerInfo();
  96. /** Name the server calls itself. */
  97. protected String sServerName;
  98. /** This is what we think the nickname should be. */
  99. protected String sThinkNickname;
  100. /** When using inbuilt pre-001 NickInUse handler, have we tried our AltNick. */
  101. protected boolean TriedAlt = false;
  102. /** Have we recieved the 001. */
  103. protected boolean Got001 = false;
  104. /** Has the thread started execution yet, (Prevents run() being called multiple times). */
  105. protected boolean HasBegan = false;
  106. /** Is this line the first line we have seen? */
  107. protected boolean IsFirst = true;
  108. /** Reference to the Processing Manager */
  109. private ProcessingManager myProcessingManager = new ProcessingManager(this);
  110. /** Reference to the callback Manager */
  111. private CallbackManager myCallbackManager = new CallbackManager(this);
  112. /**
  113. * Get a reference to the CallbackManager
  114. *
  115. * @return Reference to the CallbackManager
  116. */
  117. public CallbackManager getCallbackManager() { return myCallbackManager; }
  118. /** Hashtable storing known prefix modes (ohv). */
  119. protected Hashtable<Character,Integer> hPrefixModes = new Hashtable<Character,Integer>();
  120. /**
  121. * Hashtable maping known prefix modes (ohv) to prefixes (@%+) - Both ways.
  122. * Prefix map contains 2 pairs for each mode. (eg @ => o and o => @)
  123. */
  124. protected Hashtable<Character,Character> hPrefixMap = new Hashtable<Character,Character>();
  125. /** Integer representing the next avaliable integer value of a prefix mode. */
  126. protected int nNextKeyPrefix = 1;
  127. /** Hashtable storing known user modes (owxis etc). */
  128. protected Hashtable<Character,Integer> hUserModes = new Hashtable<Character,Integer>();
  129. /** Integer representing the next avaliable integer value of a User mode */
  130. protected int nNextKeyUser = 1;
  131. /**
  132. * Hashtable storing known boolean chan modes (cntmi etc).
  133. * Valid Boolean Modes are stored as Hashtable.pub('m',1); where 'm' is the mode and 1 is a numeric value.<br><br>
  134. * Numeric values are powers of 2. This allows up to 32 modes at present (expandable to 64)<br><br>
  135. * ChannelInfo/ChannelClientInfo etc provide methods to view the modes in a human way.<br><br>
  136. * <br>
  137. * Channel modes discovered but not listed in 005 are stored as boolean modes automatically (and a errWarning Error is called)
  138. */
  139. protected Hashtable<Character,Integer> hChanModesBool = new Hashtable<Character,Integer>();
  140. /** Integer representing the next avaliable integer value of a Boolean mode. */
  141. protected int nNextKeyCMBool = 1;
  142. /**
  143. * Hashtable storing known non-boolean chan modes (klbeI etc).
  144. * Non Boolean Modes (for Channels) are stored together in this hashtable, the value param
  145. * is used to show the type of variable. (List (1), Param just for set (2), Param for Set and Unset (2+4=6))<br><br>
  146. *<br>
  147. * see cmList<br>
  148. * see cmSet<br>
  149. * see cmUnset<br>
  150. */
  151. protected Hashtable<Character,Byte> hChanModesOther = new Hashtable<Character,Byte>();
  152. /** Byte used to show that a non-boolean mode is a list (b). */
  153. protected static final byte cmList = 1;
  154. /** Byte used to show that a non-boolean mode is not a list, and requires a parameter to set (lk). */
  155. protected static final byte cmSet = 2;
  156. /** Byte used to show that a non-boolean mode is not a list, and requires a parameter to unset (k). */
  157. protected static final byte cmUnset = 4;
  158. /**
  159. * Channel Prefixes (ie # + etc).
  160. * The "value" for these is always true.
  161. */
  162. protected Hashtable<Character,Boolean> hChanPrefix = new Hashtable<Character,Boolean>();
  163. /** Hashtable storing all known clients based on nickname (in lowercase). */
  164. protected Hashtable<String,ClientInfo> hClientList = new Hashtable<String,ClientInfo>();
  165. /** Hashtable storing all known channels based on chanel name (inc prefix - in lowercase). */
  166. protected Hashtable<String,ChannelInfo> hChannelList = new Hashtable<String,ChannelInfo>();
  167. /** Reference to the ClientInfo object that references ourself. */
  168. protected ClientInfo cMyself = null;
  169. /** Hashtable storing all information gathered from 005. */
  170. protected Hashtable<String,String> h005Info = new Hashtable<String,String>();
  171. /** This is the default TrustManager for SSL Sockets, it trusts all ssl certs. */
  172. private TrustManager[] trustAllCerts = new TrustManager[]{
  173. new X509TrustManager() {
  174. public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
  175. public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { }
  176. public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { }
  177. }
  178. };
  179. /** This is the TrustManager used for SSL Sockets. */
  180. private TrustManager[] myTrustManager = trustAllCerts;
  181. /**
  182. * Get a reference to the default TrustManager for SSL Sockets.
  183. *
  184. * @return a reference to trustAllCerts
  185. */
  186. public TrustManager[] getDefaultTrustManager() { return trustAllCerts; }
  187. /**
  188. * Get a reference to the current TrustManager for SSL Sockets.
  189. *
  190. * @return a reference to myTrustManager;
  191. */
  192. public TrustManager[] getTrustManager() { return myTrustManager; }
  193. /**
  194. * Replace the current TrustManager for SSL Sockets with a new one.
  195. *
  196. * @param newTrustManager Replacement TrustManager for SSL Sockets.
  197. */
  198. public void setTrustManager(TrustManager[] newTrustManager) { myTrustManager = newTrustManager; }
  199. /** Ignore List */
  200. protected RegexStringList myIgnoreList = new RegexStringList();
  201. /**
  202. * Get a reference to the ignorelist
  203. *
  204. * @return a reference to the ignorelist
  205. */
  206. public RegexStringList getIgnoreList() { return myIgnoreList; }
  207. //---------------------------------------------------------------------------
  208. // Start Callbacks
  209. //---------------------------------------------------------------------------
  210. /**
  211. * Callback to all objects implementing the DataIn Callback.
  212. *
  213. * @see IDataIn
  214. * @param data Incomming Line.
  215. * @return true if a method was called, false otherwise
  216. */
  217. protected boolean callDataIn(String data) {
  218. CallbackOnDataIn cb = (CallbackOnDataIn)myCallbackManager.getCallbackType("OnDataIn");
  219. if (cb != null) { return cb.call(data); }
  220. return false;
  221. }
  222. /**
  223. * Callback to all objects implementing the DataOut Callback.
  224. *
  225. * @see IDataOut
  226. * @param data Outgoing Data
  227. * @param FromParser True if parser sent the data, false if sent using .sendLine
  228. * @return true if a method was called, false otherwise
  229. */
  230. protected boolean callDataOut(String data, boolean FromParser) {
  231. CallbackOnDataOut cb = (CallbackOnDataOut)myCallbackManager.getCallbackType("OnDataOut");
  232. if (cb != null) { return cb.call(data, FromParser); }
  233. return false;
  234. }
  235. /**
  236. * Callback to all objects implementing the DebugInfo Callback.
  237. *
  238. * @see IDebugInfo
  239. * @param level Debugging Level (ndInfo, ndSocket etc)
  240. * @param data Debugging Information as a format string
  241. * @param args Formatting String Options
  242. * @return true if a method was called, false otherwise
  243. */
  244. protected boolean callDebugInfo(int level, String data, Object... args) {
  245. return callDebugInfo(level, String.format(data, args));
  246. }
  247. /**
  248. * Callback to all objects implementing the DebugInfo Callback.
  249. *
  250. * @see IDebugInfo
  251. * @param level Debugging Level (ndInfo, ndSocket etc)
  252. * @param data Debugging Information
  253. * @return true if a method was called, false otherwise
  254. */
  255. protected boolean callDebugInfo(int level, String data) {
  256. CallbackOnDebugInfo cb = (CallbackOnDebugInfo)myCallbackManager.getCallbackType("OnDebugInfo");
  257. if (cb != null) { return cb.call(level, String.format(data)); }
  258. return false;
  259. }
  260. /**
  261. * Callback to all objects implementing the IErrorInfo Interface.
  262. *
  263. * @see IErrorInfo
  264. * @param errorInfo ParserError object representing the error.
  265. * @return true if a method was called, false otherwise
  266. */
  267. protected boolean callErrorInfo(ParserError errorInfo) {
  268. CallbackOnErrorInfo cb = (CallbackOnErrorInfo)myCallbackManager.getCallbackType("OnErrorInfo");
  269. if (cb != null) { return cb.call(errorInfo); }
  270. return false;
  271. }
  272. /**
  273. * Callback to all objects implementing the SocketClosed Callback.
  274. *
  275. * @see ISocketClosed
  276. * @return true if a method was called, false otherwise
  277. */
  278. protected boolean callSocketClosed() {
  279. CallbackOnSocketClosed cb = (CallbackOnSocketClosed)myCallbackManager.getCallbackType("OnSocketClosed");
  280. if (cb != null) { return cb.call(); }
  281. return false;
  282. }
  283. //---------------------------------------------------------------------------
  284. // End Callbacks
  285. //---------------------------------------------------------------------------
  286. /**
  287. * Default constructor, ServerInfo and MyInfo need to be added separately (using IRC.me and IRC.server)
  288. */
  289. public IRCParser() { }
  290. /**
  291. * Constructor with ServerInfo, MyInfo needs to be added separately (using IRC.me)
  292. *
  293. * @param serverDetails Server information.
  294. */
  295. public IRCParser(ServerInfo serverDetails) { this(null,serverDetails); }
  296. /**
  297. * Constructor with MyInfo, ServerInfo needs to be added separately (using IRC.server)
  298. *
  299. * @param myDetails Client information.
  300. */
  301. public IRCParser(MyInfo myDetails) { this(myDetails,null); }
  302. /**
  303. * Constructor with ServerInfo and MyInfo.
  304. *
  305. * @param serverDetails Server information.
  306. * @param myDetails Client information.
  307. */
  308. public IRCParser(MyInfo myDetails, ServerInfo serverDetails) {
  309. if (myDetails != null) { this.me = myDetails; }
  310. if (serverDetails != null) { this.server = serverDetails; }
  311. }
  312. /** Reset internal state (use before connect). */
  313. private void resetState() {
  314. // Reset General State info
  315. TriedAlt = false;
  316. Got001 = false;
  317. // Clear the hash tables
  318. hChannelList.clear();
  319. hClientList.clear();
  320. h005Info.clear();
  321. hPrefixModes.clear();
  322. hPrefixMap.clear();
  323. hChanModesOther.clear();
  324. hChanModesBool.clear();
  325. hUserModes.clear();
  326. hChanPrefix.clear();
  327. // Reset the mode indexes
  328. nNextKeyPrefix = 1;
  329. nNextKeyCMBool = 1;
  330. nNextKeyUser = 1;
  331. }
  332. /**
  333. * Connect to IRC.
  334. *
  335. * @throws Exception if there was an error setting up the socket
  336. */
  337. private void connect() throws Exception {
  338. try {
  339. resetState();
  340. callDebugInfo(ndSocket, "Connecting to "+server.sHost+":"+server.nPort);
  341. if (server.bSSL) {
  342. callDebugInfo(ndSocket, "Server is SSL.");
  343. if (myTrustManager == null) { myTrustManager = trustAllCerts; }
  344. SSLContext sc = SSLContext.getInstance("SSL");
  345. sc.init(null, myTrustManager, new java.security.SecureRandom());
  346. SocketFactory socketFactory = sc.getSocketFactory();
  347. sslSocket = (SSLSocket)socketFactory.createSocket(server.sHost,server.nPort);
  348. socket = sslSocket;
  349. } else {
  350. socket = new Socket(server.sHost,server.nPort);
  351. }
  352. callDebugInfo(ndSocket, "\t-> Opening socket output stream PrintWriter");
  353. out = new PrintWriter(socket.getOutputStream(), true);
  354. nSocketState = stateOpen;
  355. callDebugInfo(ndSocket, "\t-> Opening socket input stream BufferedReader");
  356. in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  357. callDebugInfo(ndSocket, "\t-> Socket Opened");
  358. } catch (Exception e) { throw e; }
  359. }
  360. /**
  361. * Send server connection strings (NICK/USER/PASS)
  362. */
  363. private void sendConnectionStrings() {
  364. if (!server.sPassword.equals("")) {
  365. sendString("PASS "+server.sPassword);
  366. }
  367. setNickname(me.sNickname);
  368. sendString("USER "+me.sUsername.toLowerCase()+" * * :"+me.sRealname);
  369. IsFirst = false;
  370. }
  371. /**
  372. * Begin execution.
  373. * Connect to server, and start parsing incomming lines
  374. */
  375. public void run() {
  376. callDebugInfo(ndInfo, "Begin Thread Execution");
  377. if (HasBegan) { return; } else { HasBegan = true; }
  378. try { connect(); } catch (Exception e) { callDebugInfo(ndSocket, "Error Connecting, Aborted"); return; }
  379. callDebugInfo(ndSocket, "Socket Connected");
  380. if (!server.waitForFirst) { sendConnectionStrings(); }
  381. // Prepare the ProcessingManager
  382. myProcessingManager.init();
  383. String line = "";
  384. while(true) {
  385. try {
  386. line = in.readLine(); // Blocking :/
  387. if (line == null) {
  388. nSocketState = stateClosed;
  389. // Empty the ProcessingManager
  390. myProcessingManager.empty();
  391. callSocketClosed();
  392. break;
  393. } else {
  394. if (IsFirst) { sendConnectionStrings(); }
  395. processLine(line);
  396. }
  397. } catch (IOException e) {
  398. nSocketState = stateClosed;
  399. // Empty the ProcessingManager
  400. myProcessingManager.empty();
  401. callSocketClosed();
  402. break;
  403. }
  404. }
  405. callDebugInfo(ndInfo, "End Thread Execution");
  406. }
  407. /** Close socket on destroy. */
  408. protected void finalize(){
  409. try { socket.close(); }
  410. catch (IOException e) { callDebugInfo(ndSocket, "Could not close socket"); }
  411. }
  412. /**
  413. * Get the trailing parameter for a line.
  414. * The parameter is everything after the first occurance of " :" ot the last token in the line after a space.
  415. *
  416. * @param line Line to get parameter for
  417. * @return Parameter of the line
  418. */
  419. protected String getParam(String line) {
  420. String[] params = null;
  421. params = line.split(" :",2);
  422. return params[params.length-1];
  423. }
  424. /**
  425. * Tokenise a line.
  426. * splits by " " up to the first " :" everything after this is a single token
  427. *
  428. * @param line Line to tokenise
  429. * @return Array of tokens
  430. */
  431. protected String[] tokeniseLine(String line) {
  432. if (line == null) {
  433. String[] tokens = new String[1];
  434. tokens[0] = "";
  435. return tokens; // Return empty string[]
  436. };
  437. String[] params = null;
  438. String[] tokens = null;
  439. params = line.split(" :",2);
  440. tokens = params[0].split(" ");
  441. if (params.length == 2) {
  442. String[] temp = new String[tokens.length+1];
  443. System.arraycopy(tokens, 0, temp, 0, tokens.length);
  444. tokens = temp;
  445. tokens[tokens.length-1] = params[1];
  446. }
  447. return tokens;
  448. }
  449. /**
  450. * Get the ClientInfo object for a person.
  451. *
  452. * @param sWho Who can be any valid identifier for a client as long as it contains a nickname (?:)nick(?!ident)(?@host)
  453. * @return ClientInfo Object for the client, or null
  454. */
  455. public ClientInfo getClientInfo(String sWho) {
  456. sWho = ClientInfo.parseHost(sWho);
  457. sWho = sWho.toLowerCase();
  458. if (hClientList.containsKey(sWho)) { return hClientList.get(sWho); } else { return null; }
  459. }
  460. /**
  461. * Get the ChannelInfo object for a channel.
  462. *
  463. * @param sWhat This is the name of the channel.
  464. * @return ChannelInfo Object for the channel, or null
  465. */
  466. public ChannelInfo getChannelInfo(String sWhat) {
  467. sWhat = sWhat.toLowerCase();
  468. if (hChannelList.containsKey(sWhat)) { return hChannelList.get(sWhat); } else { return null; }
  469. }
  470. /**
  471. * Send a line to the server.
  472. *
  473. * @param line Line to send (\r\n termination is added automatically)
  474. */
  475. public void sendLine(String line) {
  476. if (out == null) { return; }
  477. callDataOut(line,false);
  478. out.printf("%s\r\n",line);
  479. }
  480. /**
  481. * Send a line to the server and add proper line ending.
  482. *
  483. * @param line Line to send (\r\n termination is added automatically)
  484. */
  485. protected void sendString(String line) {
  486. if (out == null) { return; }
  487. callDataOut(line,true);
  488. out.printf("%s\r\n",line);
  489. }
  490. /**
  491. * Process a line and call relevent methods for handling.
  492. *
  493. * @param line IRC Line to process
  494. */
  495. private void processLine(String line) {
  496. String[] token = tokeniseLine(line);
  497. int nParam;
  498. callDataIn(line);
  499. String sParam = token[1];
  500. try {
  501. if (token[0].equalsIgnoreCase("PING") || token[1].equalsIgnoreCase("PING")) { sendString("PONG :"+sParam); }
  502. else {
  503. if (!Got001) {
  504. // Before 001 we don't care about much.
  505. try { nParam = Integer.parseInt(token[1]); } catch (Exception e) { nParam = -1; }
  506. switch (nParam) {
  507. case 1: // 001 - Welcome to IRC
  508. case 464: // Password Required
  509. case 433: // Nick In Use
  510. try { myProcessingManager.process(sParam, token); } catch (Exception e) { }
  511. break;
  512. default: // Unknown - Send to Notice Auth
  513. try { myProcessingManager.process("Notice Auth", token); } catch (Exception e) { }
  514. break;
  515. }
  516. } else {
  517. // After 001 we potentially care about everything!
  518. try { myProcessingManager.process(sParam, token); }
  519. catch (Exception e) { /* No Processor found */ }
  520. }
  521. }
  522. } catch (Exception e) {
  523. ParserError ei = new ParserError(ParserError.errFatal, "Exception in Parser. {"+line+"}");
  524. ei.setException(e);
  525. callErrorInfo(ei);
  526. }
  527. }
  528. /**
  529. * Get the known boolean chanmodes in 005 order.
  530. * Modes are returned in the order that the ircd specifies the modes in 005
  531. * with any newly-found modes (mode being set that wasn't specified in 005)
  532. * being added at the end.
  533. *
  534. * @return All the currently known boolean modes
  535. */
  536. public String getBoolChanModes005() {
  537. // This code isn't the nicest, as Hashtable's don't lend themselves to being
  538. // ordered.
  539. // Order isn't really important, and this code only takes 3 lines of we
  540. // don't care about it but ordered guarentees that on a specific ircd this
  541. // method will ALWAYs return the same value.
  542. char[] modes = new char[hChanModesBool.size()];
  543. Character cTemp;
  544. Integer nTemp;
  545. for (Enumeration e = hChanModesBool.keys(); e.hasMoreElements();) {
  546. cTemp = (Character)e.nextElement();
  547. nTemp = hChanModesBool.get(cTemp);
  548. // Is there an easier way to find out the power of 2 value for a number?
  549. // ie 1024 = 10, 512 = 9 ?
  550. for (int i = 0; i < modes.length; i++) {
  551. if (Math.pow(2, i) == (double)nTemp) {
  552. modes[i] = cTemp;
  553. break;
  554. }
  555. }
  556. }
  557. return new String(modes);
  558. }
  559. /**
  560. * Process CHANMODES from 005.
  561. */
  562. protected void parseChanModes() {
  563. final String sDefaultModes = "b,k,l,imnpstrc";
  564. String[] Bits = null;
  565. String ModeStr;
  566. if (h005Info.containsKey("CHANMODES")) { ModeStr = h005Info.get("CHANMODES"); }
  567. else { ModeStr = sDefaultModes; h005Info.put("CHANMODES",ModeStr); }
  568. Bits = ModeStr.split(",",5);
  569. if (Bits.length < 4) {
  570. ModeStr = sDefaultModes;
  571. callErrorInfo(new ParserError(ParserError.errError, "CHANMODES String not valid. Using default string of \""+ModeStr+"\""));
  572. h005Info.put("CHANMODES",ModeStr);
  573. Bits = ModeStr.split(",",5);
  574. }
  575. // resetState
  576. hChanModesOther.clear();
  577. hChanModesBool.clear();
  578. nNextKeyCMBool = 1;
  579. // List modes.
  580. for (int i = 0; i < Bits[0].length(); ++i) {
  581. Character cMode = Bits[0].charAt(i);
  582. callDebugInfo(ndInfo, "Found List Mode: %c",cMode);
  583. if (!hChanModesOther.containsKey(cMode)) { hChanModesOther.put(cMode,cmList); }
  584. }
  585. // Param for Set and Unset.
  586. Byte nBoth = (cmSet+cmUnset);
  587. for (int i = 0; i < Bits[1].length(); ++i) {
  588. Character cMode = Bits[1].charAt(i);
  589. callDebugInfo(ndInfo, "Found Set/Unset Mode: %c",cMode);
  590. if (!hChanModesOther.containsKey(cMode)) { hChanModesOther.put(cMode,nBoth); }
  591. }
  592. // Param just for Set
  593. for (int i = 0; i < Bits[2].length(); ++i) {
  594. Character cMode = Bits[2].charAt(i);
  595. callDebugInfo(ndInfo, "Found Set Only Mode: %c",cMode);
  596. if (!hChanModesOther.containsKey(cMode)) { hChanModesOther.put(cMode,cmSet); }
  597. }
  598. // Boolean Mode
  599. for (int i = 0; i < Bits[3].length(); ++i) {
  600. Character cMode = Bits[3].charAt(i);
  601. callDebugInfo(ndInfo, "Found Boolean Mode: %c [%d]",cMode,nNextKeyCMBool);
  602. if (!hChanModesBool.containsKey(cMode)) {
  603. hChanModesBool.put(cMode,nNextKeyCMBool);
  604. nNextKeyCMBool = nNextKeyCMBool*2;
  605. }
  606. }
  607. }
  608. /**
  609. * Get the known boolean chanmodes in alphabetical order.
  610. * Modes are returned in alphabetic order
  611. *
  612. * @return All the currently known boolean modes
  613. */
  614. public String getBoolChanModes() {
  615. char[] modes = new char[hChanModesBool.size()];
  616. int i = 0;
  617. for (Enumeration e = hChanModesBool.keys(); e.hasMoreElements();) {
  618. modes[i++] = (Character)e.nextElement();
  619. }
  620. // Alphabetically sort the array
  621. Arrays.sort(modes);
  622. return new String(modes);
  623. }
  624. /**
  625. * Get the known List chanmodes.
  626. * Modes are returned in alphabetical order
  627. *
  628. * @return All the currently known List modes
  629. */
  630. public String getListChanModes() {
  631. return getOtherModeString(cmList);
  632. }
  633. /**
  634. * Get the known Set-Only chanmodes.
  635. * Modes are returned in alphabetical order
  636. *
  637. * @return All the currently known Set-Only modes
  638. */
  639. public String getSetOnlyChanModes() {
  640. return getOtherModeString(cmSet);
  641. }
  642. /**
  643. * Get the known Set-Unset chanmodes.
  644. * Modes are returned in alphabetical order
  645. *
  646. * @return All the currently known Set-Unset modes
  647. */
  648. public String getSetUnsetChanModes() {
  649. return getOtherModeString((byte)(cmSet+cmUnset));
  650. }
  651. /**
  652. * Get modes from hChanModesOther that have a specific value.
  653. * Modes are returned in alphabetical order
  654. *
  655. * @param nValue Value mode must have to be included
  656. * @return All the currently known Set-Unset modes
  657. */
  658. protected String getOtherModeString(byte nValue) {
  659. char[] modes = new char[hChanModesOther.size()];
  660. Character cTemp;
  661. Byte nTemp;
  662. int i = 0;
  663. for (Enumeration e = hChanModesOther.keys(); e.hasMoreElements();) {
  664. cTemp = (Character)e.nextElement();
  665. nTemp = hChanModesOther.get(cTemp);
  666. if (nTemp == nValue) { modes[i++] = cTemp; }
  667. }
  668. // Alphabetically sort the array
  669. Arrays.sort(modes);
  670. return (new String(modes)).trim();
  671. }
  672. /**
  673. * Process USERMODES from 004.
  674. */
  675. protected void parseUserModes() {
  676. final String sDefaultModes = "nwdoi";
  677. String[] Bits = null;
  678. String ModeStr;
  679. if (h005Info.containsKey("USERMODES")) { ModeStr = h005Info.get("USERMODES"); }
  680. else { ModeStr = sDefaultModes; h005Info.put("USERMODES", sDefaultModes); }
  681. // resetState
  682. hUserModes.clear();
  683. nNextKeyUser = 1;
  684. // Boolean Mode
  685. for (int i = 0; i < ModeStr.length(); ++i) {
  686. Character cMode = ModeStr.charAt(i);
  687. callDebugInfo(ndInfo, "Found User Mode: %c [%d]",cMode,nNextKeyUser);
  688. if (!hUserModes.containsKey(cMode)) {
  689. hUserModes.put(cMode,nNextKeyUser);
  690. nNextKeyUser = nNextKeyUser*2;
  691. }
  692. }
  693. }
  694. /**
  695. * Process CHANTYPES from 005.
  696. */
  697. protected void parseChanPrefix() {
  698. final String sDefaultModes = "#&";
  699. String[] Bits = null;
  700. String ModeStr;
  701. if (h005Info.containsKey("CHANTYPES")) { ModeStr = h005Info.get("CHANTYPES"); }
  702. else { ModeStr = sDefaultModes; h005Info.put("CHANTYPES", sDefaultModes); }
  703. // resetState
  704. hChanPrefix.clear();
  705. // Boolean Mode
  706. for (int i = 0; i < ModeStr.length(); ++i) {
  707. Character cMode = ModeStr.charAt(i);
  708. callDebugInfo(ndInfo, "Found Chan Prefix: %c",cMode);
  709. if (!hChanPrefix.containsKey(cMode)) { hChanPrefix.put(cMode,true); }
  710. }
  711. }
  712. /**
  713. * Process PREFIX from 005.
  714. */
  715. public void parsePrefixModes() {
  716. final String sDefaultModes = "(ohv)@%+";
  717. String[] Bits = null;
  718. String ModeStr;
  719. if (h005Info.containsKey("PREFIX")) { ModeStr = h005Info.get("PREFIX"); }
  720. else { ModeStr = sDefaultModes; }
  721. if (ModeStr.substring(0,1).equals("(")) { ModeStr = ModeStr.substring(1); }
  722. else { ModeStr = sDefaultModes.substring(1); h005Info.put("PREFIX", sDefaultModes); }
  723. Bits = ModeStr.split("\\)",2);
  724. if (Bits.length != 2 || Bits[0].length() != Bits[1].length()) {
  725. ModeStr = sDefaultModes;
  726. callErrorInfo(new ParserError(ParserError.errError, "PREFIX String not valid. Using default string of \""+ModeStr+"\""));
  727. h005Info.put("PREFIX",ModeStr);
  728. ModeStr = ModeStr.substring(1);
  729. Bits = ModeStr.split("\\)",2);
  730. }
  731. // resetState
  732. hPrefixModes.clear();
  733. hPrefixMap.clear();
  734. nNextKeyPrefix = 1;
  735. for (int i = Bits[0].length()-1; i > -1; --i) {
  736. Character cMode = Bits[0].charAt(i);
  737. Character cPrefix = Bits[1].charAt(i);
  738. callDebugInfo(ndInfo, "Found Prefix Mode: %c => %c [%d]",cMode,cPrefix,nNextKeyPrefix);
  739. if (!hPrefixModes.containsKey(cMode)) {
  740. hPrefixModes.put(cMode,nNextKeyPrefix);
  741. hPrefixMap.put(cMode,cPrefix);
  742. hPrefixMap.put(cPrefix,cMode);
  743. nNextKeyPrefix = nNextKeyPrefix*2;
  744. }
  745. }
  746. }
  747. /**
  748. * Check if server is ready.
  749. *
  750. * @return true if 001 has been recieved, false otherwise.
  751. */
  752. public boolean isReady() { return Got001; }
  753. /**
  754. * Join a Channel.
  755. *
  756. * @param sChannelName Name of channel to join
  757. */
  758. public void joinChannel(String sChannelName) {
  759. if (!isValidChannelName(sChannelName)) { return; }
  760. sendString("JOIN "+sChannelName);
  761. }
  762. /**
  763. * Leave a Channel.
  764. *
  765. * @param sChannelName Name of channel to part
  766. * @param sReason Reason for leaving (Nothing sent if sReason is "")
  767. */
  768. public void partChannel(String sChannelName, String sReason) {
  769. if (getChannelInfo(sChannelName) == null) { return; }
  770. if (sReason.equals("")) { sendString("PART "+sChannelName); }
  771. else { sendString("PART "+sChannelName+" :"+sReason); }
  772. }
  773. /**
  774. * Set Nickname.
  775. *
  776. * @param sNewNickName New nickname wanted.
  777. */
  778. public void setNickname(String sNewNickName) {
  779. if (this.getSocketState() != stateOpen) {
  780. me.sNickname = sNewNickName;
  781. } else {
  782. if (cMyself != null) {
  783. if (cMyself.getNickname().equals(sNewNickName)) { return; }
  784. }
  785. sendString("NICK "+sNewNickName);
  786. }
  787. sThinkNickname = sNewNickName;
  788. }
  789. /**
  790. * Send a private message to a target.
  791. *
  792. * @param sTarget Target
  793. * @param sMessage Message to send
  794. */
  795. public void sendMessage(String sTarget, String sMessage) {
  796. if (sTarget.equals("") || sMessage.equals("")) { return; }
  797. sendString("PRIVMSG "+sTarget+" :"+sMessage);
  798. }
  799. /**
  800. * Send a notice message to a target.
  801. *
  802. * @param sTarget Target
  803. * @param sMessage Message to send
  804. */
  805. public void sendNotice(String sTarget, String sMessage) {
  806. if (sTarget.equals("") || sMessage.equals("")) { return; }
  807. sendString("NOTICE "+sTarget+" :"+sMessage);
  808. }
  809. /**
  810. * Send a Action to a target.
  811. *
  812. * @param sTarget Target
  813. * @param sMessage Action to send
  814. */
  815. public void sendAction(String sTarget, String sMessage) {
  816. if (sTarget.equals("") || sMessage.equals("")) { return; }
  817. sendCTCP(sTarget, "ACTION", sMessage);
  818. }
  819. /**
  820. * Send a CTCP to a target.
  821. *
  822. * @param sTarget Target
  823. * @param sType Type of CTCP
  824. * @param sMessage Optional Additional Parameters
  825. */
  826. public void sendCTCP(String sTarget, String sType, String sMessage) {
  827. if (sTarget.equals("") || sType.equals("")) { return; }
  828. Character Char1 = Character.valueOf((char)1);
  829. if (!sMessage.equals("")) { sMessage = " "+sMessage; }
  830. sendString("PRIVMSG "+sTarget+" :"+Char1+sType.toUpperCase()+sMessage+Char1);
  831. }
  832. /**
  833. * Send a CTCPReply to a target.
  834. *
  835. * @param sTarget Target
  836. * @param sType Type of CTCP
  837. * @param sMessage Optional Additional Parameters
  838. */
  839. public void sendCTCPReply(String sTarget, String sType, String sMessage) {
  840. if (sTarget.equals("") || sType.equals("")) { return; }
  841. Character Char1 = Character.valueOf((char)1);
  842. if (!sMessage.equals("")) { sMessage = " "+sMessage; }
  843. sendString("NOTICE "+sTarget+" :"+Char1+sType.toUpperCase()+sMessage+Char1);
  844. }
  845. /**
  846. * Quit IRC.
  847. * This method will wait for the server to close the socket.
  848. *
  849. * @param sReason Reason for quitting.
  850. */
  851. public void quit(String sReason) {
  852. if (sReason.equals("")) { sendString("QUIT"); }
  853. else { sendString("QUIT :"+sReason); }
  854. }
  855. /**
  856. * Disconnect from server.
  857. * This method will quit and automatically close the socket without waiting for
  858. * the server.
  859. *
  860. * @param sReason Reason for quitting.
  861. */
  862. public void disconnect(String sReason) {
  863. quit(sReason);
  864. try { socket.close(); } catch (Exception e) { /* Meh */ };
  865. }
  866. /**
  867. * Check if a channel name is valid.
  868. *
  869. * @param sChannelName Channel name to test
  870. * @return true if name is valid on the current connection, false otherwise. (Always false before noMOTD/MOTDEnd)
  871. */
  872. public boolean isValidChannelName(String sChannelName) {
  873. return hChanPrefix.containsKey(sChannelName.charAt(0));
  874. }
  875. /**
  876. * Get a reference to the cMyself object.
  877. *
  878. * @return cMyself reference
  879. */
  880. public ClientInfo getMyself() { return cMyself; }
  881. /**
  882. * Get SVN Version information.
  883. *
  884. * @return SVN Version String
  885. */
  886. public static String getSvnInfo () { return "$Id$"; }
  887. }