/*
Eteria IRC Client, an RFC 1459 compliant client program written in Java.
Copyright (C) 2000-2001 Javier Kohen <jkohen at tough.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.Observable;
import java.util.Observer;
import java.text.ParseException;
import ar.com.jkohen.irc.Message;
import ar.com.jkohen.irc.MircMessage;
import ar.com.jkohen.net.*;
import ar.com.jkohen.util.ConfigurationProperties;
public class ServerThread extends Thread implements ServerProcess, Observer
{
private Socket sock;
private ClientProcess client;
private BufferedInputStream reader;
private BufferedOutputStream writer;
private String decoding, encoding;
private boolean disconnected;
/* Configuration properties. */
private boolean filter_mirc_attribs = false;
byte buffer[] = new byte[2048];
public long time;
public long delay;
public ServerThread(ClientProcess cl, Socket sk, String dec, String enc, int ping) throws IOException
{
client = cl;
sock = sk;
decoding = dec;
encoding = enc;
delay = ping * 1000;
InputStream is = sock.getInputStream();
OutputStream os = sock.getOutputStream();
this.reader = new BufferedInputStream(is);
this.writer = new BufferedOutputStream(os);
try
{
// FIXME: this doesn't seem to be working.
// Prevents the lock-up that occurs when the route to the peer is dropped.
sock.setSoLinger(true, 30);
}
catch (SocketException ex) { System.err.println(ex); }
}
public void run()
{
while (!disconnected)
{
try
{
processNextLine();
}
catch (IOException e)
{
client.report(e);
}
}
}
private void processNextLine() throws IOException
{
String str = readLine();
time = System.currentTimeMillis();
if (str != null)
{
try
{
Message m;
if (filter_mirc_attribs)
m = new MircMessage(str);
else
m = new Message(str);
client.processMessage(m);
}
catch (ParseException e)
{
System.err.println(e);
}
}
}
public String readLine()
{
boolean eol = false;
int i = 0, n = 0;
while (!eol)
{
try
{
i = reader.read();
}
catch (IOException ex)
{
i = -1;
if (!disconnected)
client.report(ex);
}
if (i == -1 || ((i == 13 || i == 10) && n > 0) || n == 2048)
{
eol = true;
}
else if (i != 13 && i != 10)
{
buffer[n++] = (byte)i;
}
}
if (i == -1)
disconnect();
if (n > 0)
return(decode(n));
else
return(null);
}
private String decode(int n)
{
if (decoding.equalsIgnoreCase("IRC"))
{
try
{
return(fromU8(n));
}
catch (ParseException e) {}
try
{
return(new String(buffer, 0, n, "ISO_8859-1"));
}
catch (UnsupportedEncodingException e) {}
}
else if (decoding.equalsIgnoreCase("UTF-8") || encoding.equalsIgnoreCase("UTF8"))
{
try
{
return(new String(buffer, 0, n, "ISO_8859-1"));
}
catch (UnsupportedEncodingException e) {}
}
else
{
try
{
return(new String(buffer, 0, n, decoding));
}
catch (UnsupportedEncodingException e) { System.out.println(e); }
}
return(new String(buffer));
}
private byte[] encode(String str)
{
if (encoding.equalsIgnoreCase("IRC"))
{
try
{
if(isLatin(str))
return(str.getBytes("ISO_8859-1"));
}
catch (UnsupportedEncodingException e) {}
try
{
return(str.getBytes("UTF8"));
}
catch (UnsupportedEncodingException e) {}
}
else if (encoding.equalsIgnoreCase("ISO_8859-1") || encoding.equalsIgnoreCase("ISO-8859-1"))
{
try
{
if(isLatin(str))
return(str.getBytes("ISO_8859-1"));
}
catch (UnsupportedEncodingException e) {}
}
else
{
try
{
return(str.getBytes(encoding));
}
catch (UnsupportedEncodingException e) { System.out.println(e); }
}
return(str.getBytes());
}
private boolean isLatin(String str)
{
char chars[] = str.toCharArray();
char c = 0;
for (int i = 0; i < chars.length; i++)
{
c = chars[i];
if ((c >= 0x7F && c <= 0x9F) || c > 0xFF)
return(false);
}
return(true);
}
private String fromU8(int num) throws ParseException
{
StringBuffer decoded = new StringBuffer(num);
int pos = 0;
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
boolean valid = false;
while(pos < num)
{
valid = false;
c1 = buffer[pos++] & 0xFF;
if (c1 < 0x80)
{
decoded.append((char)c1);
valid = true;
}
else if (c1 < 0xC2)
{
valid = false;
}
else if (c1 < 0xE0 && pos < num)
{
c2 = buffer[pos++] & 0xFF;
if ((c2 & 0xC0) == 0x80)
{
c1 = (c1 & 0x1F) << 6;
c2 &= 0x3F;
decoded.append((char)(c1 | c2));
valid = true;
}
}
else if (c1 < 0xF0 && (pos + 1 < num))
{
c2 = buffer[pos++] & 0xFF;
c3 = buffer[pos++] & 0xFF;
if ((c2 & c3 & 0xC0) == 0x80)
{
c1 = (c1 & 0x0F) << 12;
c2 = (c2 & 0x3F) << 6;
c3 &= 0x3F;
decoded.append((char)(c1 | c2 | c3));
valid = true;
}
}
else if (c1 < 0xF5 && (pos + 2 < num))
{
c2 = buffer[pos++] & 0xFF;
c3 = buffer[pos++] & 0xFF;
c4 = buffer[pos++] & 0xFF;
if ((c2 & c3 & c4 & 0xC0) == 0x80)
{
c1 = (c1 & 0x07) << 18;
c2 = (c2 & 0x3F) << 12;
c3 = (c3 & 0x3F) << 6;
c4 &= 0x3F;
decoded.append((char)(c1 | c2 | c3 | c4));
valid = true;
}
}
if (!valid)
throw new ParseException("UTF-8 decoding error", pos);
}
return(decoded.toString());
}
public void enqueueMessage(Message m) throws IOException
{
String [] slices = m.slices();
for (int i = 0; i < slices.length; i++)
{
writer.write(encode(slices[i]));
writer.write(13);
}
writer.flush();
}
public boolean timedOut()
{
return((System.currentTimeMillis() - time) > delay);
}
public void disconnect()
{
if (disconnected)
return;
disconnected = true;
try
{
writer.close();
}
catch (IOException e) {}
try
{
reader.close();
}
catch (IOException e) {}
try
{
sock.close();
}
catch (IOException e) {}
}
public Socket getSocket()
{
return(sock);
}
public void update(Observable o, Object arg)
{
ConfigurationProperties props = (ConfigurationProperties) o;
if (arg == null || arg.equals("filter_mirc_attribs"))
this.filter_mirc_attribs = props.getBoolean("filter_mirc_attribs");
}
}