]> git.somenet.org - irc/pjirc-ng.git/blob - src/main/java/irc/IRCServer.java
Pjirc 2.2.1 as available on the net, reformatted and made it compile.
[irc/pjirc-ng.git] / src / main / java / irc / IRCServer.java
1 /*****************************************************/
2 /*          This java file is a part of the          */
3 /*                                                   */
4 /*           -  Plouf's Java IRC Client  -           */
5 /*                                                   */
6 /*   Copyright (C)  2002 - 2005 Philippe Detournay   */
7 /*                                                   */
8 /*         All contacts : theplouf@yahoo.com         */
9 /*                                                   */
10 /*  PJIRC is free software; you can redistribute     */
11 /*  it and/or modify it under the terms of the GNU   */
12 /*  General Public License as published by the       */
13 /*  Free Software Foundation; version 2 or later of  */
14 /*  the License.                                     */
15 /*                                                   */
16 /*  PJIRC is distributed in the hope that it will    */
17 /*  be useful, but WITHOUT ANY WARRANTY; without     */
18 /*  even the implied warranty of MERCHANTABILITY or  */
19 /*  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   */
20 /*  General Public License for more details.         */
21 /*                                                   */
22 /*  You should have received a copy of the GNU       */
23 /*  General Public License along with PJIRC; if      */
24 /*  not, write to the Free Software Foundation,      */
25 /*  Inc., 59 Temple Place, Suite 330, Boston,        */
26 /*  MA  02111-1307  USA                              */
27 /*                                                   */
28 /*****************************************************/
29
30 package irc;
31
32 import irc.dcc.prv.DCCChatServer;
33 import irc.dcc.prv.DCCFileHandler;
34
35 import java.io.File;
36 import java.util.Date;
37 import java.util.Enumeration;
38 import java.util.Hashtable;
39 import java.util.Vector;
40
41 /**
42  * FirstLineFilter, used to handle CTCP codes.
43  */
44 class FirstLineFilter {
45         private IRCServer _server;
46         private IRCConfiguration _ircConfiguration;
47         private ServerManager _mgr;
48
49         /**
50          * Create a new FirstLineFilter
51          * 
52          * @param serv
53          * @param mgr
54          * @param config
55          */
56         public FirstLineFilter(IRCServer serv, ServerManager mgr, IRCConfiguration config) {
57                 _ircConfiguration = config;
58                 _mgr = mgr;
59                 _server = serv;
60         }
61
62         /**
63          * Release this object.
64          */
65         public void release() {
66                 _mgr = null;
67                 _server = null;
68         }
69
70         /**
71          * Perform any needed action from a channel message.
72          * 
73          * @param channel
74          *          channel name.
75          * @param nick
76          *          nickname.
77          * @param msg
78          *          actual message.
79          * @return true if message was handled, false otherwise.
80          */
81         public boolean performFromChannelMessage(String channel, String nick, String msg) {
82                 if (!msg.startsWith("\1"))
83                         return false;
84
85                 msg = msg.substring(1);
86                 msg = msg.substring(0, msg.length() - 1);
87                 String cmd = "";
88                 String param = "";
89                 int pos = msg.indexOf(' ');
90                 if (pos == -1) {
91                         cmd = msg.toLowerCase(java.util.Locale.ENGLISH);
92                 } else {
93                         cmd = msg.substring(0, pos).toLowerCase(java.util.Locale.ENGLISH);
94                         param = msg.substring(pos + 1);
95                 }
96
97                 if (cmd.equals("action")) {
98                         Channel c = _server.getChannel(channel, false);
99                         if (c != null)
100                                 c.action(nick, param);
101                 } else if (cmd.equals("sound")) {
102                         _ircConfiguration.getAudioConfiguration().play(param);
103                         _server.sendStatusMessage("\2\3" + "4" + "[" + nick + " " + cmd.toUpperCase(java.util.Locale.ENGLISH) + "]");
104                 }
105                 return true;
106         }
107
108         /**
109          * Perform any needed action from a nick message.
110          * 
111          * @param nick
112          *          nickname.
113          * @param msg
114          *          actual message.
115          * @return true if message was handled, false otherwise.
116          */
117         public boolean performFromNickMessage(String nick, String msg) {
118                 if (!msg.startsWith("\1"))
119                         return false;
120
121                 msg = msg.substring(1);
122                 msg = msg.substring(0, msg.length() - 1);
123                 String cmd = "";
124                 String param = "";
125                 int pos = msg.indexOf(' ');
126                 if (pos == -1) {
127                         cmd = msg.toLowerCase(java.util.Locale.ENGLISH);
128                 } else {
129                         cmd = msg.substring(0, pos).toLowerCase(java.util.Locale.ENGLISH);
130                         param = msg.substring(pos + 1);
131                 }
132
133                 boolean show = true;
134                 if (cmd.equals("action")) {
135                         show = false;
136                         Query q = _server.getQuery(nick, false);
137                         if (q != null)
138                                 q.action(nick, param);
139                 } else if (cmd.equals("version")) {
140                         String data = "PJIRC " + _ircConfiguration.getVersion();
141                         _server.execute("NOTICE " + nick + " :\1VERSION " + data + "\1");
142                 } else if (cmd.equals("ping")) {
143                         _server.execute("NOTICE " + nick + " :\1PING " + param + "\1");
144                 } else if (cmd.equals("time")) {
145                         String data = new Date().toString();
146                         _server.execute("NOTICE " + nick + " :\1TIME " + data + "\1");
147                 } else if (cmd.equals("finger")) {
148                         String data = _ircConfiguration.getS("fingerreply");
149                         _server.execute("NOTICE " + nick + " :\1FINGER " + data + "\1");
150                 } else if (cmd.equals("userinfo")) {
151                         String data = _ircConfiguration.getS("userinforeply");
152                         _server.execute("NOTICE " + nick + " :\1USERINFO " + data + "\1");
153                 } else if (cmd.equals("clientinfo")) {
154                         String data = "This client is a Java application supporting the following CTCP tags : ACTION VERSION PING TIME FINGER USERINFO CLIENTINFO SOUND DCC";
155                         _server.execute("NOTICE " + nick + " :\1CLIENTINFO " + data + "\1");
156                 } else if (cmd.equals("sound")) {
157                         _ircConfiguration.getAudioConfiguration().play(param);
158                 } else if (cmd.equals("dcc")) {
159                         StringParser sp = new StringParser();
160                         String[] args = sp.parseString(param.toLowerCase(java.util.Locale.ENGLISH));
161                         if (args.length >= 2) {
162                                 if (args[0].equals("chat") && args[1].equals("chat") && _ircConfiguration.getB("allowdccchat")) {
163                                         if (args.length >= 4) {
164                                                 boolean bres = false;
165                                                 Object[] res = _server.specialRequest("DCCChatRequest", new Object[] { nick });
166                                                 for (int i = 0; i < res.length; i++)
167                                                         if (((Boolean) res[i]).booleanValue())
168                                                                 bres = true;
169
170                                                 if (bres) {
171                                                         try {
172                                                                 DCCChatServer cserver = new DCCChatServer(_ircConfiguration, _server.getNick(), nick);
173                                                                 cserver.openActive(args[2], args[3]);
174                                                                 _mgr.newServer(cserver, false);
175                                                         } catch (Throwable ex) {
176                                                                 ex.printStackTrace();
177                                                         }
178                                                 }
179                                         }
180                                 }
181                                 if (args[0].equals("send") && _ircConfiguration.getB("allowdccfile")) {
182                                         if (args.length >= 5) {
183                                                 String fname = args[1];
184                                                 String ip = args[2];
185                                                 String port = args[3];
186                                                 String size = args[4];
187                                                 Object[] res = _server.specialRequest("DCCFileRequest", new Object[] { nick, fname, new Integer(size) });
188                                                 File dest = null;
189                                                 if (res.length > 0)
190                                                         dest = (File) res[0];
191                                                 if (dest != null) {
192                                                         try {
193                                                                 DCCFileHandler handler = new DCCFileHandler(_ircConfiguration, nick, dest);
194                                                                 handler.receive(ip, port, size);
195                                                                 _mgr.newServer(handler, false);
196                                                         } catch (Throwable ex) {
197                                                                 ex.printStackTrace();
198                                                         }
199                                                 }
200                                         }
201                                 }
202                         }
203                 }
204                 if (show)
205                         _server.sendStatusMessage("\2\3" + "4" + "[" + nick + " " + cmd.toUpperCase(java.util.Locale.ENGLISH) + "]");
206                 return true;
207         }
208
209         /**
210          * Perform any needed action from a notice message.
211          * 
212          * @param nick
213          *          nickname.
214          * @param msg
215          *          actual message.
216          * @return true if message was handled, false otherwise.
217          */
218         public boolean performFromNotice(String nick, String msg) {
219                 if (!msg.startsWith("\1"))
220                         return false;
221
222                 msg = msg.substring(1);
223                 msg = msg.substring(0, msg.length() - 1);
224                 String cmd = "";
225                 String param = "";
226                 int pos = msg.indexOf(' ');
227                 if (pos == -1) {
228                         cmd = msg.toLowerCase(java.util.Locale.ENGLISH);
229                 } else {
230                         cmd = msg.substring(0, pos).toLowerCase(java.util.Locale.ENGLISH);
231                         param = msg.substring(pos + 1);
232                 }
233
234                 Source source = _server.getDefaultSource();
235                 if (cmd.equals("ping")) {
236                         long d = (new Long(param)).longValue();
237                         long delta = (new Date()).getTime() - d;
238                         if (source != null)
239                                 source.report("\2\3" + "4"
240                                                 + _ircConfiguration.getText(IRCTextProvider.CTCP_PING_REPLY, nick, (delta / 1000.0) + ""));
241                         return true;
242                 }
243                 if (source != null)
244                         source.report("\2\3" + "4" + "[" + nick + " " + cmd.toUpperCase(java.util.Locale.ENGLISH) + " reply] : " + param);
245                 return true;
246         }
247 }
248
249 /**
250  * The IRC server.
251  */
252 public class IRCServer extends IRCObject implements Server, ServerProtocolListener {
253         private ServerProtocol _protocol;
254         private Hashtable _channels;
255         private Hashtable _queries;
256         private Hashtable _chanlist;
257         private Status _status;
258
259         private Hashtable _ignoreList;
260
261         private ListenerGroup _listeners;
262         private ListenerGroup _replylisteners;
263         private ListenerGroup _messagelisteners;
264         private String[] _askedNick;
265         private String _nick;
266         private String _userName;
267         private int _tryNickIndex;
268         private ModeHandler _mode;
269         private String[] _host;
270         private int[] _port;
271         private String _passWord[];
272         private int _tryServerIndex;
273         private boolean _connected;
274         private String _name;
275         private Source _defaultSource;
276         private boolean _serverLeaving;
277         private boolean _registered;
278         private FirstLineFilter _filter;
279         // private boolean _nickWaiting=false;
280
281         private char[] _nickModes = { 'o', 'h', 'v' };
282         private char[] _nickPrefixes = { '@', '%', '+' };
283         private char[] _channelPrefixes = { '#', '&', '!', '+' };
284         private char[][] _globalModes = { { 'b' }, { 'k' }, { 'l' }, { 'i', 'm', 'n', 'p', 's', 't', 'a', 'q', 'r' } };
285
286         /**
287          * Create a new IRCServer.
288          * 
289          * @param config
290          *          global IRCConfiguration.
291          * @param mgr
292          *          the server manager.
293          * @param nick
294          *          claimed nick.
295          * @param altNick
296          *          claimed alternate nick.
297          * @param userName
298          *          user name.
299          * @param name
300          *          the server name.
301          */
302         public IRCServer(IRCConfiguration config, ServerManager mgr, String nick, String altNick, String userName, String name) {
303                 super(config);
304                 _filter = new FirstLineFilter(this, mgr, config);
305                 _serverLeaving = false;
306                 _name = name;
307                 _userName = userName;
308                 _askedNick = new String[2];
309                 _askedNick[0] = nick;
310                 _askedNick[1] = altNick;
311                 _nick = nick;
312                 _connected = false;
313                 _ignoreList = new Hashtable();
314
315                 _channels = new Hashtable();
316                 _queries = new Hashtable();
317                 _chanlist = new Hashtable();
318
319                 _listeners = new ListenerGroup();
320                 _replylisteners = new ListenerGroup();
321                 _messagelisteners = new ListenerGroup();
322
323                 _status = new Status(_ircConfiguration, this);
324                 _defaultSource = _status;
325
326                 _protocol = new ServerProtocol(_ircConfiguration);
327                 _protocol.addServerProtocolListener(this);
328
329                 _host = null;
330                 _mode = new ModeHandler(_globalModes, _nickModes);
331         }
332
333         /**
334          * Send a special request event to all listeners.
335          * 
336          * @param request
337          *          request string.
338          * @param params
339          *          request parameters.
340          * @return results.
341          */
342         public Object[] specialRequest(String request, Object[] params) {
343                 return _listeners.sendEvent("specialServerRequest", request, this, params);
344         }
345
346         @Override
347         public void release() {
348                 _protocol.removeServerProtocolListener(this);
349                 _filter.release();
350                 super.release();
351         }
352
353         @Override
354         public Enumeration getSources() {
355                 Vector v = new Vector();
356                 Enumeration e;
357                 e = _channels.elements();
358                 while (e.hasMoreElements())
359                         v.insertElementAt(e.nextElement(), v.size());
360                 e = _queries.elements();
361                 while (e.hasMoreElements())
362                         v.insertElementAt(e.nextElement(), v.size());
363                 e = _chanlist.elements();
364                 while (e.hasMoreElements())
365                         v.insertElementAt(e.nextElement(), v.size());
366                 if (_status != null)
367                         v.insertElementAt(_status, v.size());
368                 return v.elements();
369         }
370
371         @Override
372         public void enumerateSourcesAsCreated(ServerListener lis) {
373                 Enumeration e;
374                 e = _channels.elements();
375                 while (e.hasMoreElements())
376                         lis.sourceCreated((Source) e.nextElement(), this, new Boolean(false));
377                 e = _queries.elements();
378                 while (e.hasMoreElements())
379                         lis.sourceCreated((Source) e.nextElement(), this, new Boolean(false));
380                 e = _chanlist.elements();
381                 while (e.hasMoreElements())
382                         lis.sourceCreated((Source) e.nextElement(), this, new Boolean(false));
383                 if (_status != null)
384                         lis.sourceCreated(_status, this, new Boolean(true));
385         }
386
387         @Override
388         public void enumerateSourcesAsRemoved(ServerListener lis) {
389                 Enumeration e;
390                 e = _channels.elements();
391                 while (e.hasMoreElements())
392                         lis.sourceRemoved((Source) e.nextElement(), this);
393                 e = _queries.elements();
394                 while (e.hasMoreElements())
395                         lis.sourceRemoved((Source) e.nextElement(), this);
396                 e = _chanlist.elements();
397                 while (e.hasMoreElements())
398                         lis.sourceRemoved((Source) e.nextElement(), this);
399                 if (_status != null)
400                         lis.sourceRemoved(_status, this);
401         }
402
403         @Override
404         public void setDefaultSource(Source s) {
405                 _defaultSource = s;
406         }
407
408         /**
409          * Get the default server source, or null if no default source is defined.
410          * 
411          * @return default source.
412          */
413         public Source getDefaultSource() {
414                 return _defaultSource;
415         }
416
417         /**
418          * Set default configuration for the next connection.
419          * 
420          * @param host
421          *          server host.
422          * @param port
423          *          server port.
424          * @param passWord
425          *          server password.
426          */
427         public void setServers(String host[], int port[], String passWord[]) {
428                 _tryServerIndex = 0;
429                 _host = new String[host.length];
430                 for (int i = 0; i < host.length; i++)
431                         _host[i] = host[i];
432                 _port = new int[port.length];
433                 for (int i = 0; i < port.length; i++)
434                         _port[i] = port[i];
435                 _passWord = new String[passWord.length];
436                 for (int i = 0; i < passWord.length; i++)
437                         _passWord[i] = passWord[i];
438         }
439
440         @Override
441         public void connect() {
442                 _tryServerIndex = 0;
443                 if (_host != null)
444                         connect(_host, _port, _passWord);
445         }
446
447         private void connect(String host[], int port[], String[] passWord) {
448                 _registered = false;
449                 // if(_nickWaiting) return;
450                 if (_tryServerIndex == _host.length)
451                         return;
452                 _tryNickIndex = 0;
453                 _passWord = passWord;
454                 if (_protocol.connecting()) {
455                         sendStatusMessage(getText(IRCTextProvider.SERVER_UNABLE_TO_CONNECT_STILL, host[_tryServerIndex],
456                                         _host[_tryServerIndex]));
457                         return;
458                 }
459                 if (_protocol.connected()) {
460                         // sendStatusMessage(getText(IRCTextProvider.SERVER_DISCONNECTED,_host[_tryServerIndex]));
461                         // disconnect();
462                         _protocol.disconnect();
463                 }
464                 _connected = false;
465                 sendStatusMessage(getText(IRCTextProvider.SERVER_CONNECTING));
466                 _protocol.connect(host[_tryServerIndex], port[_tryServerIndex]);
467         }
468
469         /**
470          * Disconnect from the irc server.
471          */
472         @Override
473         public void disconnect() {
474                 // if(_nickWaiting) return;
475                 if (_protocol.connected()) {
476                         if (_ircConfiguration.getS("quitmessage").length() == 0) {
477                                 execute("QUIT");
478                         } else {
479                                 execute("QUIT :" + _ircConfiguration.get("quitmessage"));
480                         }
481                 } else {
482                         sendStatusMessage(getText(IRCTextProvider.SERVER_NOT_CONNECTED));
483                 }
484         }
485
486         /**
487          * Return true if connected to the server, false otherwise.
488          * 
489          * @return connected state.
490          */
491         @Override
492         public boolean isConnected() {
493                 return _connected;
494         }
495
496         @Override
497         public void connectionFailed(String message, String host) {
498                 sendStatusMessage(getText(IRCTextProvider.SERVER_UNABLE_TO_CONNECT, message));
499                 _tryServerIndex++;
500                 if (_tryServerIndex < _host.length)
501                         connect(_host, _port, _passWord);
502         }
503
504         private void nickUsed() {
505                 if (_tryNickIndex >= _askedNick.length) {
506                         // _nickWaiting=true;
507                         Object[] res = _listeners.sendEvent("cannotUseRequestedNicknames", new Object[] { this });
508                         if (res.length > 0)
509                                 _askedNick = (String[]) res[0];
510                         // _nickWaiting=false;
511                         _tryNickIndex = 0;
512                 } else if (_askedNick[_tryNickIndex].indexOf("?") == -1)
513                         _tryNickIndex++;
514         }
515
516         private void register() {
517                 String tryUseNick = _askedNick[_tryNickIndex];
518                 if (tryUseNick.length() == 0)
519                         tryUseNick = "Anon????";
520                 String ans = "";
521                 for (int i = 0; i < tryUseNick.length(); i++) {
522                         char c = tryUseNick.charAt(i);
523                         if (c == '?')
524                                 c = (char) ('0' + Math.random() * 10);
525                         ans += c;
526                 }
527                 if (_passWord[_tryServerIndex].length() > 0)
528                         execute("pass " + _passWord[_tryServerIndex]);
529                 execute("nick " + ans);
530                 String name = _ircConfiguration.getS("userid");
531                 if (name.length() == 0)
532                         name = ans;
533                 if (!_registered) {
534                         _registered = true;
535                         execute("user " + name + " 0 0 :" + _userName);
536                 }
537         }
538
539         /**
540          * Get the local port of the remote connection.
541          * 
542          * @return the local, client-side port of the remote connection.
543          */
544         public int getLocalPort() {
545                 return _protocol.getLocalPort();
546         }
547
548         @Override
549         public void connected(String host) {
550                 sendStatusMessage(getText(IRCTextProvider.SERVER_LOGIN));
551                 register();
552         }
553
554         private void clear(Hashtable l) {
555                 Enumeration e;
556                 e = l.elements();
557                 while (e.hasMoreElements())
558                         _listeners.sendEvent("sourceRemoved", e.nextElement(), this);
559                 e = l.elements();
560                 while (e.hasMoreElements())
561                         ((Source) e.nextElement()).release();
562                 l.clear();
563         }
564
565         @Override
566         public void disconnected(String host) {
567                 sendStatusMessage(getText(IRCTextProvider.SERVER_DISCONNECTED, host));
568
569                 clear(_channels);
570                 clear(_queries);
571                 clear(_chanlist);
572
573                 _mode.reset();
574                 if (_status != null)
575                         _status.modeChanged(getMode());
576                 // _defaultSource=null;
577
578                 _connected = false;
579                 _listeners.sendEvent("serverDisconnected", this);
580
581                 if (_serverLeaving) {
582                         _listeners.sendEvent("sourceRemoved", _status, this);
583                         deleteStatus("");
584                         _listeners.sendEvent("serverLeft", this);
585                 }
586         }
587
588         @Override
589         public void sendStatusMessage(String msg) {
590                 if (_status != null)
591                         _status.report(msg);
592         }
593
594         /**
595          * Get all the channels.
596          * 
597          * @return an enumeration of channels.
598          */
599         public Enumeration getChannels() {
600                 return _channels.elements();
601         }
602
603         /**
604          * Get all the queries.
605          * 
606          * @return an enumeration of queries.
607          */
608         public Enumeration getQueries() {
609                 return _queries.elements();
610         }
611
612         /**
613          * Get all the chanlists.
614          * 
615          * @return an enumeration of chanlists.
616          */
617         public Enumeration getChanLists() {
618                 return _chanlist.elements();
619         }
620
621         /**
622          * Get the channel from its name. If this channel doesn't exist, it is created
623          * only if create boolean is set.
624          * 
625          * @param name
626          *          channel name.
627          * @param create
628          *          true if channel must be created if not existing.
629          * @return channel, or null.
630          */
631         public Channel getChannel(String name, boolean create) {
632                 Channel c = (Channel) _channels.get(name.toLowerCase(java.util.Locale.ENGLISH));
633                 if ((c == null) && create) {
634                         c = new Channel(_ircConfiguration, name, this);
635                         _channels.put(name.toLowerCase(java.util.Locale.ENGLISH), c);
636                         _listeners.sendEvent("sourceCreated", c, this, new Boolean(true));
637                 }
638                 return c;
639         }
640
641         /**
642          * Get the query from its name. If this query doesn't exist, it is created.
643          * The query cannot be get if the server is not connected.
644          * 
645          * @param nick
646          *          query name.
647          * @param local
648          *          true if this query has been created following a local request.
649          * @return query, or null if server was not connected.
650          */
651         public Query getQuery(String nick, boolean local) {
652                 if (!_connected)
653                         return null;
654                 if (_ircConfiguration.getB("disablequeries"))
655                         return null;
656                 Query c = (Query) _queries.get(nick.toLowerCase(java.util.Locale.ENGLISH));
657                 if (c == null) {
658                         c = new Query(_ircConfiguration, nick, this);
659                         _queries.put(nick.toLowerCase(java.util.Locale.ENGLISH), c);
660                         _listeners.sendEvent("sourceCreated", c, this, new Boolean(local));
661
662                 }
663                 return c;
664         }
665
666         /**
667          * Get the chanlist from its name. If this chanlist doesn't exist, it is
668          * created.
669          * 
670          * @param name
671          *          chanlist name.
672          * @return channel.
673          */
674         private ChanList getChanList(String name) {
675                 ChanList c = (ChanList) _chanlist.get(name.toLowerCase(java.util.Locale.ENGLISH));
676                 if (c == null) {
677                         c = new ChanList(_ircConfiguration, this, name);
678                         _chanlist.put(name.toLowerCase(java.util.Locale.ENGLISH), c);
679                         _listeners.sendEvent("sourceCreated", c, this, new Boolean(true));
680                 }
681                 return c;
682         }
683
684         /**
685          * Request to leave the given channel.
686          * 
687          * @param name
688          *          channel name.
689          */
690         public void leaveChannel(String name) {
691                 execute("part " + name);
692         }
693
694         /**
695          * Request to leave the given query.
696          * 
697          * @param name
698          *          query name.
699          */
700         public void leaveQuery(String name) {
701                 Query q = getQuery(name, false);
702                 if (q == null)
703                         return;
704                 _listeners.sendEvent("sourceRemoved", q, this);
705                 deleteQuery(name);
706         }
707
708         @Override
709         public void leave() {
710                 leaveStatus("");
711         }
712
713         /**
714          * Request to leave the status. This will cause server leaving.
715          * 
716          * @param name
717          *          Status name. Unused.
718          */
719         public void leaveStatus(String name) {
720                 if (_status == null)
721                         return;
722                 if (isConnected()) {
723                         _serverLeaving = true;
724                         disconnect();
725                 } else {
726                         _listeners.sendEvent("sourceRemoved", _status, this);
727                         deleteStatus("");
728                         _listeners.sendEvent("serverLeft", this);
729                 }
730                 /*
731                  * long time=System.currentTimeMillis(); while(isConnected()) { try {
732                  * Thread.sleep(100); if(System.currentTimeMillis()-time>10000) break; }
733                  * catch(InterruptedException ex) { } }
734                  */
735                 /*
736                  * _listeners.sendEvent("sourceRemoved",_status,this); deleteStatus(name);
737                  * _listeners.sendEvent("serverLeft",this);
738                  */
739         }
740
741         /**
742          * Request to leave the given channel list.
743          * 
744          * @param name
745          *          chanlist name.
746          */
747         public void leaveChanList(String name) {
748                 _listeners.sendEvent("sourceRemoved", getChanList(name), this);
749                 deleteChanList(name);
750         }
751
752         private void deleteSource(Source src) {
753                 if (src == _defaultSource)
754                         _defaultSource = null;
755                 src.release();
756         }
757
758         private void deleteChannel(String name) {
759                 deleteSource((Source) _channels.remove(name.toLowerCase(java.util.Locale.ENGLISH)));
760         }
761
762         private void deleteQuery(String name) {
763                 deleteSource((Source) _queries.remove(name.toLowerCase(java.util.Locale.ENGLISH)));
764         }
765
766         private void deleteChanList(String name) {
767                 deleteSource((Source) _chanlist.remove(name.toLowerCase(java.util.Locale.ENGLISH)));
768         }
769
770         private void deleteStatus(String name) {
771                 deleteSource(_status);
772                 _status = null;
773         }
774
775         @Override
776         public String getServerName() {
777                 if (_name.length() == 0) {
778                         if (_tryServerIndex < _host.length)
779                                 return _host[_tryServerIndex];
780                         return _host[0];
781                 }
782
783                 return _name;
784         }
785
786         /**
787          * Get this server's status, or null if this server has no status.
788          * 
789          * @return the status, or null if the server hasno status.
790          */
791         public Status getStatus() {
792                 return _status;
793         }
794
795         /**
796          * Add a server listener.
797          * 
798          * @param l
799          *          listener to add.
800          */
801         @Override
802         public void addServerListener(ServerListener l) {
803                 _listeners.addListener(l);
804         }
805
806         /**
807          * Remove a listener.
808          * 
809          * @param l
810          *          listener to remove.
811          */
812         @Override
813         public void removeServerListener(ServerListener l) {
814                 _listeners.removeListener(l);
815         }
816
817         /**
818          * Add a reply listener.
819          * 
820          * @param l
821          *          listener to add.
822          */
823         public void addReplyServerListener(ReplyServerListener l) {
824                 _replylisteners.addListener(l);
825         }
826
827         /**
828          * Add a message listener.
829          * 
830          * @param l
831          *          listener to add.
832          */
833         public void addMessageServerListener(MessageServerListener l) {
834                 _messagelisteners.addListener(l);
835         }
836
837         /**
838          * Remove a reply listener.
839          * 
840          * @param l
841          *          listener to remove.
842          */
843         public void removeReplyServerListener(ReplyServerListener l) {
844                 _replylisteners.removeListener(l);
845         }
846
847         /**
848          * Remove a message listener.
849          * 
850          * @param l
851          *          listener to remove.
852          */
853         public void removeMessageServerListener(MessageServerListener l) {
854                 _messagelisteners.removeListener(l);
855         }
856
857         /**
858          * Get an array of all known channel prefixes.
859          * 
860          * @return an array of all channel prefixes.
861          */
862         public char[] getChannelPrefixes() {
863                 return _channelPrefixes;
864         }
865
866         /**
867          * Get an array of all known nickname prefixes.
868          * 
869          * @return array of all nickname prefixes.
870          */
871         public char[] getNickPrefixes() {
872                 return _nickPrefixes;
873         }
874
875         /**
876          * Get an array of all known nickname modes.
877          * 
878          * @return array of all nickname modes.
879          */
880         public char[] getNickModes() {
881                 return _nickModes;
882         }
883
884         /**
885          * Get an array of all known A,B,C,D channel modes.
886          * 
887          * @return array of all channel modes. This is an array of four char arrays.
888          */
889         public char[][] getChannelModes() {
890                 return _globalModes;
891         }
892
893         /**
894          * Get the nick prefix associated with the given nick mode.
895          * 
896          * @param mode
897          *          nick mode.
898          * @return nick prefix for this mode.
899          */
900         public String getNickPrefix(String mode) {
901                 if (mode.length() == 0)
902                         return "";
903                 char cmode = mode.charAt(0);
904                 for (int i = 0; i < _nickModes.length; i++)
905                         if (_nickModes[i] == cmode)
906                                 return "" + _nickPrefixes[i];
907                 return "";
908         }
909
910         /**
911          * Get the nick mode associated with the given nick prefix.
912          * 
913          * @param prefix
914          *          nick prefix.
915          * @return nick mode for this prefix.
916          */
917         public String getNickMode(String prefix) {
918                 if (prefix.length() == 0)
919                         return "";
920                 char cprefix = prefix.charAt(0);
921                 for (int i = 0; i < _nickPrefixes.length; i++)
922                         if (_nickPrefixes[i] == cprefix)
923                                 return "" + _nickModes[i];
924                 return "";
925         }
926
927         private void setNicks(Channel c, Vector nicks) {
928                 String[] n = new String[nicks.size()];
929                 String[] modes = new String[nicks.size()];
930
931                 for (int i = 0; i < nicks.size(); i++) {
932                         n[i] = (String) nicks.elementAt(i);
933                         modes[i] = "";
934                         if (n[i].length() > 0) {
935                                 modes[i] = getNickMode("" + n[i].charAt(0));
936                                 if (modes[i].length() != 0)
937                                         n[i] = n[i].substring(1);
938                         }
939                 }
940                 c.setNicks(n, modes);
941         }
942
943         private void decodeVariable(String key, String val) {
944                 if (key.toLowerCase(java.util.Locale.ENGLISH).equals("prefix")) {
945                         if (!val.startsWith("("))
946                                 return;
947                         int pos = val.indexOf(")");
948                         if (pos < 0)
949                                 return;
950                         String modes = val.substring(1, pos);
951                         String prefixes = val.substring(pos + 1);
952                         if (prefixes.length() != modes.length())
953                                 return;
954
955                         _nickModes = new char[modes.length()];
956                         for (int i = 0; i < modes.length(); i++)
957                                 _nickModes[i] = modes.charAt(i);
958                         _nickPrefixes = new char[modes.length()];
959                         for (int i = 0; i < prefixes.length(); i++)
960                                 _nickPrefixes[i] = prefixes.charAt(i);
961                 } else if (key.toLowerCase(java.util.Locale.ENGLISH).equals("chantypes")) {
962                         _channelPrefixes = new char[val.length()];
963                         for (int i = 0; i < _channelPrefixes.length; i++)
964                                 _channelPrefixes[i] = val.charAt(i);
965                 } else if (key.toLowerCase(java.util.Locale.ENGLISH).equals("chanmodes")) {
966                         int pos = val.indexOf(',');
967                         if (pos < 0)
968                                 return;
969                         String a = val.substring(0, pos);
970                         val = val.substring(pos + 1);
971                         pos = val.indexOf(',');
972                         if (pos < 0)
973                                 return;
974                         String b = val.substring(0, pos);
975                         val = val.substring(pos + 1);
976                         pos = val.indexOf(',');
977                         if (pos < 0)
978                                 return;
979                         String c = val.substring(0, pos);
980                         String d = val.substring(pos + 1);
981                         _globalModes = new char[4][];
982                         _globalModes[0] = new char[a.length()];
983                         for (int i = 0; i < a.length(); i++)
984                                 _globalModes[0][i] = a.charAt(i);
985                         _globalModes[1] = new char[b.length()];
986                         for (int i = 0; i < b.length(); i++)
987                                 _globalModes[1][i] = b.charAt(i);
988                         _globalModes[2] = new char[c.length()];
989                         for (int i = 0; i < c.length(); i++)
990                                 _globalModes[2][i] = c.charAt(i);
991                         _globalModes[3] = new char[d.length()];
992                         for (int i = 0; i < d.length(); i++)
993                                 _globalModes[3][i] = d.charAt(i);
994                 }
995         }
996
997         private void learnServerVariables(String var[]) {
998                 for (int i = 1; i < var.length; i++) {
999                         String v = var[i];
1000                         int pos = v.indexOf('=');
1001                         String key;
1002                         String val;
1003                         if (pos < 0) {
1004                                 key = v;
1005                                 val = "";
1006                         } else {
1007                                 key = v.substring(0, pos);
1008                                 val = v.substring(pos + 1);
1009                         }
1010                         decodeVariable(key, val);
1011                 }
1012                 _mode = new ModeHandler(_globalModes, _nickModes);
1013         }
1014
1015         @Override
1016         public void replyReceived(String prefix, String id, String params[]) {
1017                 Object[] b = _replylisteners.sendEvent("replyReceived", new Object[] { prefix, id, params, this });
1018                 for (int i = 0; i < b.length; i++)
1019                         if (((Boolean) b[i]).booleanValue())
1020                                 return;
1021
1022                 if (id.equals("324")) // mode : RPL_CHANNELMODEIS
1023                 {
1024                         Channel c = getChannel(params[1], false);
1025                         if (c != null) {
1026                                 String mode = "";
1027                                 for (int i = 2; i < params.length; i++)
1028                                         mode += " " + params[i];
1029                                 mode = mode.substring(1);
1030                                 c.applyMode(mode, "");
1031                         }
1032                 } else if (id.equals("332")) // topic : RPL_TOPIC
1033                 {
1034                         Channel c = getChannel(params[1], false);
1035                         if (c != null)
1036                                 c.setTopic(params[2], "");
1037                 } else if (id.equals("353")) // names : RPL_NAMREPLY
1038                 {
1039                         int first = 1;
1040                         if (params[1].length() == 1)
1041                                 first++;
1042                         Channel c = getChannel(params[first], false);
1043                         if (c != null) {
1044                                 String nick = "";
1045                                 Vector nicks = new Vector();
1046                                 for (int i = 0; i < params[first + 1].length(); i++) {
1047                                         char u = params[first + 1].charAt(i);
1048                                         if (u == ' ') {
1049                                                 if (nick.length() > 0)
1050                                                         nicks.insertElementAt(nick, nicks.size());
1051                                                 nick = "";
1052                                         } else {
1053                                                 nick += u;
1054                                         }
1055                                 }
1056                                 if (nick.length() > 0)
1057                                         nicks.insertElementAt(nick, nicks.size());
1058                                 setNicks(c, nicks);
1059                         }
1060                 } else if (id.equals("001")) // RPL_WELCOME
1061                 {
1062                         String nick = params[0];
1063                         if (!(nick.equals(_nick))) {
1064                                 _nick = nick;
1065                                 if (_status != null)
1066                                         _status.nickChanged(nick);
1067                         }
1068                         _connected = true;
1069                         _listeners.sendEvent("serverConnected", this);
1070                 } else if (id.equals("005")) // RPL_ISUPPORT
1071                 {
1072                         learnServerVariables(params);
1073                 } else if (id.equals("321")) // /list begin : RPL_LISTSTART
1074                 {
1075                         getChanList(_host[_tryServerIndex]).begin();
1076                 } else if (id.equals("322")) // /list : RPL_LIST
1077                 {
1078                         String name = params[1];
1079                         int count = new Integer(params[2]).intValue();
1080                         if ((count < 32767) && (isChannel(name))) {
1081                                 String topic = params[3];
1082                                 getChanList(_host[_tryServerIndex]).addChannel(new ChannelInfo(name, topic, count));
1083                         }
1084                 } else if (id.equals("323")) // /list end : RPL_LISTEND
1085                 {
1086                         getChanList(_host[_tryServerIndex]).end();
1087                 } else if (id.equals("433")) // nick used : ERR_NICKNAMEINUSE
1088                 {
1089                         if (!_connected) {
1090                                 nickUsed();
1091                                 register();
1092                         }
1093                 }
1094                 // We failed to rejoin the channel
1095                 // ERR_INVITEONLYCHAN || ERR_CHANNELISFULL || ERR_NOSUCHCHANNEL ||
1096                 // ERR_BANNEDFROMCHAN || ERR_BADCHANNELKEY || ERR_BADCHANMASK ||
1097                 // ERR_TOOMANYCHANNELS
1098                 else if (id.equals("473") || id.equals("471") || id.equals("403") || id.equals("474") || id.equals("475")
1099                                 || id.equals("476") || id.equals("405")) {
1100                         String cname = params[1];
1101                         Channel channel = getChannel(cname, false);
1102                         if (channel != null) {
1103                                 sendStatusMessage(getText(IRCTextProvider.SERVER_AUTOREJOIN_FAILED, cname));
1104                                 _listeners.sendEvent("sourceRemoved", channel, this);
1105                                 deleteChannel(cname);
1106                         }
1107                 }
1108                 // We're performing an action on a channel we're not into
1109                 else if (id.equals("442")) // ERR_NOTONCHANNEL
1110                 {
1111                         Channel chan = getChannel(params[1], false);
1112                         if (chan != null) {
1113                                 _listeners.sendEvent("sourceRemoved", chan, this);
1114                                 deleteChannel(chan.getName());
1115                         }
1116                 } else {
1117                         /*
1118                          * String toSend=""; for(int i=1;i<params.length;i++)
1119                          * toSend+=" "+params[i]; toSend=toSend.substring(1);
1120                          * sendStatusMessage(toSend);
1121                          */
1122                 }
1123
1124         }
1125
1126         private String extractNick(String full) {
1127                 int pos = full.indexOf('!');
1128                 if (pos == -1)
1129                         return full;
1130                 return full.substring(0, pos);
1131         }
1132
1133         private boolean isChannel(String name) {
1134                 if (name.length() == 0)
1135                         return false;
1136                 for (int i = 0; i < _channelPrefixes.length; i++)
1137                         if (name.charAt(0) == _channelPrefixes[i])
1138                                 return true;
1139                 return false;
1140         }
1141
1142         private void globalNickRemove(String nick, String reason) {
1143                 Enumeration e = _channels.elements();
1144                 while (e.hasMoreElements()) {
1145                         Channel c = (Channel) e.nextElement();
1146                         if (c.hasNick(nick))
1147                                 c.quitNick(nick, reason);
1148                 }
1149         }
1150
1151         private void globalNickChange(String oldNick, String newNick) {
1152                 Enumeration e;
1153                 e = _channels.elements();
1154                 while (e.hasMoreElements()) {
1155                         Channel c = (Channel) e.nextElement();
1156                         if (c.hasNick(oldNick))
1157                                 c.changeNick(oldNick, newNick);
1158                 }
1159
1160                 Query q = (Query) _queries.get(oldNick.toLowerCase(java.util.Locale.ENGLISH));
1161                 if (q != null) {
1162                         _queries.remove(oldNick.toLowerCase(java.util.Locale.ENGLISH));
1163                         q.changeNick(newNick);
1164                         Query existing = (Query) _queries.get(newNick.toLowerCase(java.util.Locale.ENGLISH));
1165                         if (existing != null)
1166                                 existing.leave();
1167                         _queries.put(newNick.toLowerCase(java.util.Locale.ENGLISH), q);
1168                 }
1169         }
1170
1171         /**
1172          * Return true if this server is ignoring the given nick, false otherwise.
1173          * 
1174          * @param nick
1175          *          nick to test.
1176          * @return the ignore status of the given nick.
1177          */
1178         public synchronized boolean ignore(String nick) {
1179                 return _ignoreList.get(nick) != null;
1180         }
1181
1182         /**
1183          * Ignore the given nick.
1184          * 
1185          * @param nick
1186          *          nick to ignore.
1187          */
1188         public synchronized void addIgnore(String nick) {
1189                 _ignoreList.put(nick, nick);
1190         }
1191
1192         /**
1193          * Remove the given list from the ignore list.
1194          * 
1195          * @param nick
1196          *          nick to remove from ignore list.
1197          */
1198         public synchronized void removeIgnore(String nick) {
1199                 _ignoreList.remove(nick);
1200         }
1201
1202         @Override
1203         public void messageReceived(String prefix, String command, String params[]) {
1204                 Object[] b = _messagelisteners.sendEvent("messageReceived", new Object[] { prefix, command, params, this });
1205                 for (int i = 0; i < b.length; i++)
1206                         if (((Boolean) b[i]).booleanValue())
1207                                 return;
1208
1209                 String toSend = "";
1210                 for (int i = 0; i < params.length; i++)
1211                         toSend += " " + params[i];
1212
1213                 command = command.toLowerCase(java.util.Locale.ENGLISH);
1214
1215                 String nick = extractNick(prefix);
1216
1217                 if (command.equals("notice")) {
1218                         if (!ignore(nick)) {
1219                                 if (!_filter.performFromNotice(nick, params[1]))
1220                                         if (_defaultSource != null)
1221                                                 _defaultSource.noticeReceived(nick, params[1]);
1222                         }
1223                 } else if (command.equals("privmsg")) {
1224                         if (!ignore(nick)) {
1225                                 if (isChannel(params[0])) {
1226                                         if (!_filter.performFromChannelMessage(params[0], nick, params[1])) {
1227                                                 Channel c = getChannel(params[0], false);
1228                                                 if (c != null)
1229                                                         c.messageReceived(nick, params[1]);
1230                                         }
1231                                 } else {
1232                                         if (!_filter.performFromNickMessage(nick, params[1])) {
1233                                                 Query q = getQuery(nick, false);
1234                                                 if (q != null)
1235                                                         q.messageReceived(nick, params[1]);
1236                                         }
1237                                 }
1238                         }
1239                 } else if (command.equals("join")) {
1240                         if (!nick.equals(getNick())) {
1241                                 Channel c = getChannel(params[0], false);
1242                                 if (c != null)
1243                                         c.joinNick(nick, "");
1244                         } else {
1245                                 Channel c = getChannel(params[0], true);
1246                                 if (c != null) {
1247                                         c.resetNicks();
1248                                         execute("mode " + params[0]);
1249                                 }
1250                         }
1251                 } else if (command.equals("part")) {
1252                         Channel c = getChannel(params[0], false);
1253                         if (c != null) {
1254                                 if (params.length > 1) {
1255                                         c.partNick(nick, params[1]);
1256                                 } else {
1257                                         c.partNick(nick, "");
1258                                 }
1259                                 if (nick.equals(getNick())) {
1260                                         _listeners.sendEvent("sourceRemoved", c, this);
1261                                         deleteChannel(c.getName());
1262                                 }
1263                         }
1264                 } else if (command.equals("kick")) {
1265                         Channel c = getChannel(params[0], false);
1266                         if (c != null) {
1267                                 String target = params[1];
1268                                 String reason = "";
1269                                 if (params.length > 2)
1270                                         reason = params[2];
1271                                 c.kickNick(target, nick, reason);
1272                                 if (target.equals(getNick())) {
1273                                         if (_ircConfiguration.getB("autorejoin")) {
1274                                                 c.report(getText(IRCTextProvider.SERVER_AUTOREJOIN_ATTEMPT, c.getName()));
1275                                                 execute("join " + params[0]);
1276                                         } else {
1277                                                 _listeners.sendEvent("sourceRemoved", c, this);
1278                                                 deleteChannel(c.getName());
1279                                         }
1280                                 }
1281                         }
1282                 } else if (command.equals("topic")) {
1283                         Channel c = getChannel(params[0], false);
1284                         if (c != null)
1285                                 c.setTopic(params[1], nick);
1286                 } else if (command.equals("mode")) {
1287                         String full = "";
1288                         for (int i = 1; i < params.length; i++)
1289                                 full += params[i] + " ";
1290                         if (isChannel(params[0])) {
1291                                 Channel c = getChannel(params[0], false);
1292                                 if (c != null) {
1293                                         MultiModeHandler h = new MultiModeHandler(full, _globalModes, _nickModes);
1294                                         while (!h.terminated()) {
1295                                                 h.next();
1296                                                 if (h.isPrefix() || h.isModeA()) {
1297                                                         c.applyUserMode(h.getParameter(), h.getMode(), nick);
1298                                                 } else {
1299                                                         if (h.hasParameter())
1300                                                                 c.applyMode(h.getMode() + " " + h.getParameter(), nick);
1301                                                         else
1302                                                                 c.applyMode(h.getMode(), nick);
1303                                                 }
1304                                         }
1305                                 }
1306                         } else if (nick.equals(getNick())) {
1307                                 _mode.apply(full);
1308                                 if (_status != null)
1309                                         _status.modeChanged(getMode());
1310                         }
1311                 } else if (command.equals("nick")) {
1312                         if (nick.equals(getNick())) {
1313                                 _nick = params[0];
1314                                 if (_status != null)
1315                                         _status.nickChanged(getNick());
1316                         }
1317                         globalNickChange(nick, params[0]);
1318                 } else if (command.equals("quit")) {
1319                         if (params.length > 0)
1320                                 globalNickRemove(nick, params[0]);
1321                         else
1322                                 globalNickRemove(nick, "");
1323                 } else if (command.equals("ping")) {
1324                         execute("pong :" + params[0]);
1325                         // sendStatusMessage("\3"+"3"+"PING? PONG!");
1326                 } else if (command.equals("invite")) {
1327                         String invited = params[0];
1328                         String channel = params[1];
1329                         if (invited.equals(getNick())) {
1330                                 if (_status != null)
1331                                         _status.invited(channel, nick);
1332
1333                                 // if(_defaultSource!=null)
1334                                 // _defaultSource.report(getText(IRCTextProvider.SOURCE_YOU_INVITED,nick,channel));
1335                         }
1336                 } else if (command.equals("error")) {
1337                         sendStatusMessage(getText(IRCTextProvider.SERVER_ERROR, params[0]));
1338                 } else {
1339                         // System.out.println("("+command+") "+prefix+" -> "+toSend);
1340                 }
1341
1342         }
1343
1344         @Override
1345         public String getNick() {
1346                 return _nick;
1347         }
1348
1349         @Override
1350         public String getUserName() {
1351                 return _userName;
1352         }
1353
1354         /**
1355          * Get the current status mode.
1356          * 
1357          * @return status mode.
1358          */
1359         public String getMode() {
1360                 return _mode.getMode();
1361         }
1362
1363         @Override
1364         public void say(String destination, String str) {
1365                 execute("PRIVMSG " + destination + " :" + str);
1366         }
1367
1368         @Override
1369         public void execute(String str) {
1370                 int pos = str.indexOf(' ');
1371                 if (pos >= 0) {
1372                         String cmd = str.substring(0, pos).toLowerCase(java.util.Locale.ENGLISH);
1373                         if (cmd.equals("join")) {
1374                                 String rem = str.substring(pos + 1);
1375                                 pos = rem.indexOf(' ');
1376                                 if (pos >= 0)
1377                                         rem = rem.substring(0, pos);
1378                                 if (!_ircConfiguration.mayJoin(rem))
1379                                         return;
1380                         } else if (cmd.equals("part")) {
1381                                 String rem = str.substring(pos + 1);
1382                                 pos = rem.indexOf(' ');
1383                                 if (pos >= 0)
1384                                         rem = rem.substring(0, pos);
1385                                 if (!_ircConfiguration.mayLeave(rem))
1386                                         return;
1387                         }
1388                 }
1389
1390                 pos = str.indexOf(' ');
1391                 if (pos > 0) {
1392                         String cmd = str.substring(0, pos).toUpperCase(java.util.Locale.ENGLISH);
1393                         String param = str.substring(pos + 1);
1394                         str = cmd + " " + param;
1395                 } else {
1396                         str = str.toUpperCase(java.util.Locale.ENGLISH);
1397                 }
1398                 sendString(str);
1399         }
1400
1401         private void sendString(String str) {
1402                 try {
1403                         _protocol.sendString(str);
1404                 } catch (Exception e) {
1405                         sendStatusMessage(getText(IRCTextProvider.SERVER_ERROR, e.getMessage()));
1406                 }
1407         }
1408
1409 }