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.
610 lines
20 KiB
610 lines
20 KiB
/* This file is part of the KDE project
|
|
*
|
|
* Copyright (C) 2003 Koos Vriezen <koos ! vriezen () xs4all ! nl>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
package org.kde.kjas.server;
|
|
|
|
import java.net.*;
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import java.security.*;
|
|
/**
|
|
*
|
|
*/
|
|
|
|
class KIOConnection
|
|
{
|
|
final static int NOT_CONNECTED = 0;
|
|
final static int CONNECT_WAIT = 1;
|
|
final static int CONNECTED = 2;
|
|
|
|
final static int DATA = 0;
|
|
final static int FINISHED = 1;
|
|
final static int ERRORCODE = 2;
|
|
final static int CONNECT = 6;
|
|
final static int REQUESTDATA = 7;
|
|
|
|
final static int STOP = 0;
|
|
final static int HOLD = 1;
|
|
final static int RESUME = 2;
|
|
|
|
protected static int id = 0;
|
|
static Hashtable jobs = new Hashtable(); // should be thread safe
|
|
|
|
static void setData(String jobid, int code, byte [] data) {
|
|
KIOConnection job = (KIOConnection) jobs.get(jobid);
|
|
if (job == null || !job.setData(code, data))
|
|
Main.info("KIO KJASHttpURLConnection gone (timedout/closed)");
|
|
else
|
|
Thread.yield();
|
|
}
|
|
|
|
private class KJASOutputStream extends OutputStream {
|
|
KJASOutputStream() {
|
|
}
|
|
public void write(int b) throws IOException {
|
|
byte[] buf = {(byte)b};
|
|
write(buf);
|
|
}
|
|
public synchronized void write(byte b[], int off, int len) throws IOException {
|
|
byte[] buf = new byte[len];
|
|
System.arraycopy(b, off, buf, 0, len);
|
|
sendData(buf, false);
|
|
}
|
|
public void write(byte b[]) throws IOException {
|
|
write(b, 0, b.length);
|
|
}
|
|
public void close() throws IOException {
|
|
disconnect();
|
|
}
|
|
public void flush() throws IOException {
|
|
checkConnected();
|
|
sendData(null, true);
|
|
}
|
|
}
|
|
|
|
private class KJASInputStream extends InputStream {
|
|
|
|
KJASInputStream() {
|
|
}
|
|
public int read() throws IOException {
|
|
if (getData(true))
|
|
return 0x00ff & in_buf[in_bufpos++];
|
|
return -1;
|
|
}
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
int total = 0;
|
|
do {
|
|
if (!getData(true)) break;
|
|
int nr = in_buf.length - in_bufpos;
|
|
if (nr > len)
|
|
nr = len;
|
|
System.arraycopy(in_buf, in_bufpos, b, off, nr);
|
|
len -= nr;
|
|
total += nr;
|
|
off += nr;
|
|
in_bufpos += nr;
|
|
} while (len > 0);
|
|
return total > 0 ? total : -1;
|
|
}
|
|
public int read(byte[] b) throws IOException {
|
|
return read(b, 0, b.length);
|
|
}
|
|
public int available() throws IOException {
|
|
return inAvailable();
|
|
}
|
|
public boolean markSupported() {
|
|
return false;
|
|
}
|
|
public void close() throws IOException {
|
|
disconnect();
|
|
}
|
|
}
|
|
|
|
protected URL url;
|
|
protected int connect_status = 0;
|
|
protected String jobid = null; // connection id with KIO
|
|
protected LinkedList data = new LinkedList(); // not thread safe
|
|
protected int errorcode = 0;
|
|
protected boolean finished = false; // all data has arived
|
|
protected boolean onhold = false; // KIO job is suspended
|
|
protected boolean request_data = false; // need data for put job
|
|
private KJASOutputStream out = null;
|
|
private KJASInputStream in = null;
|
|
private byte [] in_buf = null; // current input buffer
|
|
private int in_bufpos = 0; // position in buffer
|
|
private boolean in_eof = false; // all data is read
|
|
private final static int LOW_BUFFER_LIMIT = 5; // put onhold off
|
|
private final static int HIGH_BUFFER_LIMIT = 10; // put onhold on
|
|
|
|
protected KIOConnection(URL u) {
|
|
url = u;
|
|
}
|
|
protected void checkConnected() throws IOException {
|
|
if (connect_status != CONNECTED)
|
|
throw new IOException("not connected");
|
|
}
|
|
protected boolean haveError() {
|
|
return errorcode != 0;
|
|
}
|
|
synchronized protected boolean setData(int code, byte [] d) {
|
|
// is job still there when entering the monitor
|
|
if (jobs.get(jobid) == null)
|
|
return false;
|
|
if (connect_status == CONNECT_WAIT)
|
|
connect_status = CONNECTED;
|
|
switch (code) {
|
|
case FINISHED:
|
|
if (d != null && d.length > 0)
|
|
data.addLast(d);
|
|
finished = true;
|
|
onhold = false;
|
|
jobs.remove(jobid);
|
|
Main.debug ("KIO FINISHED (" + jobid + ") " + data.size());
|
|
break;
|
|
case DATA:
|
|
if (d.length > 0)
|
|
data.addLast(d);
|
|
// Main.debug ("KIO DATA (" + jobid + ") " + data.size());
|
|
if (!onhold && data.size() > HIGH_BUFFER_LIMIT) {
|
|
Main.protocol.sendDataCmd(jobid, HOLD);
|
|
onhold = true;
|
|
}
|
|
break;
|
|
case ERRORCODE:
|
|
String codestr = new String(d);
|
|
errorcode = Integer.parseInt(codestr);
|
|
Main.debug ("KIO ERRORECODE(" + jobid + ") " + errorcode);
|
|
break;
|
|
case CONNECT:
|
|
Main.debug ("KIO CONNECT(" + jobid + ") ");
|
|
request_data = true;
|
|
errorcode = 0;
|
|
break;
|
|
case REQUESTDATA:
|
|
Main.debug ("KIO REQUESTDATA(" + jobid + ") ");
|
|
request_data = true;
|
|
break;
|
|
}
|
|
notifyAll();
|
|
return true;
|
|
}
|
|
|
|
private synchronized boolean getData(boolean mayblock) throws IOException {
|
|
if (haveError()) {
|
|
//disconnect();
|
|
in_eof = true;
|
|
//throw new IOException("i/o error " + errorcode);
|
|
}
|
|
if (in_eof)
|
|
return false;
|
|
checkConnected();
|
|
if (in_buf != null && in_bufpos < in_buf.length)
|
|
return true;
|
|
int datasize = data.size();
|
|
if (datasize > 0) {
|
|
in_buf = (byte []) data.removeFirst();
|
|
in_bufpos = 0;
|
|
}
|
|
if (onhold && datasize < LOW_BUFFER_LIMIT) {
|
|
Main.protocol.sendDataCmd(jobid, RESUME);
|
|
onhold = false;
|
|
}
|
|
if (datasize > 0)
|
|
return true;
|
|
if (finished) {
|
|
in_eof = true;
|
|
return false;
|
|
}
|
|
if (!mayblock)
|
|
return false;
|
|
try {
|
|
wait();
|
|
} catch (InterruptedException ie) {
|
|
return false;
|
|
}
|
|
return getData(false);
|
|
}
|
|
synchronized private int inAvailable() throws IOException {
|
|
if (in_eof)
|
|
return 0;
|
|
checkConnected();
|
|
if (!getData(false))
|
|
return 0;
|
|
int total = in_buf.length - in_bufpos;
|
|
ListIterator it = data.listIterator(0);
|
|
while (it.hasNext())
|
|
total += ((byte []) it.next()).length;
|
|
return total;
|
|
}
|
|
synchronized private void sendData(byte [] d, boolean force) throws IOException {
|
|
Main.debug ("KIO sendData(" + jobid + ") force:" + force + " request_data:" + request_data);
|
|
if (d != null)
|
|
data.addLast(d);
|
|
if (!request_data && !force) return;
|
|
if (data.size() == 0) return;
|
|
if (force && !request_data) {
|
|
try {
|
|
wait(10000);
|
|
} catch (InterruptedException ie) {
|
|
return;
|
|
}
|
|
if (!request_data) {
|
|
Main.debug ("KIO sendData(" + jobid + ") timeout");
|
|
data.clear();
|
|
disconnect();
|
|
throw new IOException("timeout");
|
|
}
|
|
}
|
|
byte[] buf;
|
|
int total = 0;
|
|
ListIterator it = data.listIterator(0);
|
|
while (it.hasNext())
|
|
total += ((byte []) it.next()).length;
|
|
buf = new byte[total];
|
|
int off = 0;
|
|
it = data.listIterator(0);
|
|
while (it.hasNext()) {
|
|
byte [] b = (byte []) it.next();
|
|
System.arraycopy(b, 0, buf, off, b.length);
|
|
off += b.length;
|
|
}
|
|
data.clear();
|
|
request_data = false;
|
|
Main.protocol.sendPutData(jobid, buf, 0, total);
|
|
}
|
|
synchronized void connect(boolean doInput) throws IOException {
|
|
if (connect_status == CONNECTED)
|
|
return; // javadocs: call is ignored
|
|
//(new Exception()).printStackTrace();
|
|
Main.debug ("KIO connect " + url);
|
|
errorcode = 0;
|
|
finished = in_eof = false;
|
|
jobid = String.valueOf(id++);
|
|
jobs.put(jobid, this);
|
|
if (doInput)
|
|
Main.protocol.sendGetURLDataCmd(jobid, url.toExternalForm());
|
|
else
|
|
Main.protocol.sendPutURLDataCmd(jobid, url.toExternalForm());
|
|
connect_status = CONNECT_WAIT;
|
|
try {
|
|
wait(20000);
|
|
} catch (InterruptedException ie) {
|
|
errorcode = -1;
|
|
}
|
|
boolean isconnected = (connect_status == CONNECTED);
|
|
if (isconnected && !haveError()) {
|
|
if (doInput)
|
|
in = new KJASInputStream();
|
|
else
|
|
out = new KJASOutputStream();
|
|
Main.debug ("KIO connect(" + jobid + ") " + url);
|
|
return;
|
|
}
|
|
connect_status = NOT_CONNECTED;
|
|
jobs.remove(jobid);
|
|
if (isconnected) {
|
|
if (!finished)
|
|
Main.protocol.sendDataCmd(jobid, STOP);
|
|
Main.debug ("KIO connect error " + url);
|
|
throw new ConnectException("connection failed (not found)");
|
|
}
|
|
Main.debug ("KIO connect timeout " + url);
|
|
throw new IOException("connection failed (timeout)");
|
|
}
|
|
synchronized void disconnect() {
|
|
if (connect_status == NOT_CONNECTED)
|
|
return;
|
|
Main.debug ("KIO disconnect " + jobid);
|
|
//(new Exception()).printStackTrace();
|
|
if (out != null) {
|
|
try {
|
|
out.flush();
|
|
} catch (IOException iox) {}
|
|
}
|
|
connect_status = NOT_CONNECTED;
|
|
out = null;
|
|
in = null;
|
|
if (!finished) {
|
|
Main.protocol.sendDataCmd(jobid, STOP);
|
|
jobs.remove(jobid);
|
|
}
|
|
notifyAll();
|
|
}
|
|
InputStream getInputStream() throws IOException {
|
|
Main.debug ("KIO getInputStream(" + jobid + ") " + url);
|
|
return in;
|
|
}
|
|
OutputStream getOutputStream() throws IOException {
|
|
Main.debug ("KIO getOutputStream(" + jobid + ") " + url);
|
|
return out;
|
|
}
|
|
}
|
|
|
|
final class KIOHttpConnection extends KIOConnection
|
|
{
|
|
final static int HEADERS = 3;
|
|
final static int REDIRECT = 4;
|
|
final static int MIMETYPE = 5;
|
|
|
|
Vector headers = new Vector();
|
|
Hashtable headersmap = new Hashtable();
|
|
String responseMessage = null;
|
|
int responseCode = -1;
|
|
|
|
KIOHttpConnection(URL u) {
|
|
super(u);
|
|
}
|
|
protected boolean haveError() {
|
|
return super.haveError() ||
|
|
responseCode != 404 && (responseCode < 0 || responseCode >= 400);
|
|
}
|
|
protected synchronized boolean setData(int code, byte [] d) {
|
|
switch (code) {
|
|
case HEADERS:
|
|
StringTokenizer tokenizer = new StringTokenizer(new String(d), "\n");
|
|
while (tokenizer.hasMoreTokens()) {
|
|
String token = tokenizer.nextToken();
|
|
int pos = token.indexOf(":");
|
|
String [] entry = {
|
|
token.substring(0, pos > -1 ? pos : token.length()).toLowerCase(), token.substring(pos > -1 ? pos+1: token.length()).trim()
|
|
};
|
|
headers.add(entry);
|
|
headersmap.put(entry[0], entry[1]);
|
|
// Main.debug ("KIO header " + entry[0] + "=" + entry[1]);
|
|
}
|
|
responseCode = 0;
|
|
if (headersmap.size() > 0) {
|
|
String token = ((String []) headers.get(0))[0];
|
|
if (!token.startsWith("http/1.")) break;
|
|
int spos = token.indexOf(' ');
|
|
if (spos < 0) break;
|
|
int epos = token.indexOf(' ', spos + 1);
|
|
if (epos < 0) break;
|
|
responseCode = Integer.parseInt(token.substring(spos+1, epos));
|
|
responseMessage = token.substring(epos+1);
|
|
Main.debug ("KIO responsecode=" + responseCode);
|
|
}
|
|
break;
|
|
}
|
|
return super.setData(code, d);
|
|
}
|
|
}
|
|
|
|
final class KIOSimpleConnection extends KIOConnection
|
|
{
|
|
KIOSimpleConnection(URL u) {
|
|
super(u);
|
|
}
|
|
}
|
|
|
|
final class KJASHttpURLConnection extends HttpURLConnection
|
|
{
|
|
private KIOHttpConnection kioconnection;
|
|
|
|
KJASHttpURLConnection(URL u) {
|
|
super(u);
|
|
kioconnection = new KIOHttpConnection(u);
|
|
}
|
|
public Map getHeaderFields() {
|
|
try {
|
|
connect();
|
|
} catch (IOException e) {
|
|
Main.debug ("Error on implicit connect()");
|
|
}
|
|
Main.debug ("KIO getHeaderFields");
|
|
return kioconnection.headersmap;
|
|
}
|
|
public String getHeaderField(String name) {
|
|
try {
|
|
connect();
|
|
} catch (IOException e) {
|
|
Main.debug ("Error on implicit connect()");
|
|
}
|
|
String field = (String) kioconnection.headersmap.get(name);
|
|
Main.debug ("KIO getHeaderField:" + name + "=" + field);
|
|
//(new Exception()).printStackTrace();
|
|
return field;
|
|
}
|
|
public String getHeaderField(int n) {
|
|
try {
|
|
connect();
|
|
} catch (IOException e) {
|
|
Main.debug ("Error on implicit connect()");
|
|
}
|
|
Main.debug ("KIO getHeaderField(" + n + ") size=" + kioconnection.headersmap.size());
|
|
if (n >= kioconnection.headersmap.size())
|
|
return null;
|
|
String [] entry = (String []) kioconnection.headers.get(n);
|
|
String line = entry[0];
|
|
if (entry[1].length() > 0)
|
|
line += ":" + entry[1];
|
|
Main.debug ("KIO getHeaderField(" + n + ")=#" + line + "#");
|
|
return line;
|
|
}
|
|
public String getHeaderFieldKey(int n) {
|
|
try {
|
|
connect();
|
|
} catch (IOException e) {
|
|
Main.debug ("Error on implicit connect()");
|
|
}
|
|
Main.debug ("KIO getHeaderFieldKey " + n);
|
|
if (n >= kioconnection.headersmap.size())
|
|
return null;
|
|
return ((String []) kioconnection.headers.get(n))[0];
|
|
}
|
|
public int getResponseCode() throws IOException {
|
|
Main.debug ("KIO getResponseCode");
|
|
if (kioconnection.responseCode == -1) {
|
|
try {
|
|
connect();
|
|
} catch (IOException e) {
|
|
if (kioconnection.responseCode == -1)
|
|
throw e;
|
|
}
|
|
}
|
|
responseMessage = kioconnection.responseMessage;
|
|
return kioconnection.responseCode;
|
|
}
|
|
public boolean usingProxy() {
|
|
return false; // FIXME
|
|
}
|
|
public void connect() throws IOException {
|
|
if (connected)
|
|
return;
|
|
Main.debug ("KIO KJASHttpURLConnection.connect " + url);
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null)
|
|
security.checkPermission(getPermission());
|
|
kioconnection.connect(doInput);
|
|
connected = true;
|
|
if (kioconnection.responseCode == 404)
|
|
throw new FileNotFoundException(url.toExternalForm());
|
|
}
|
|
public void disconnect() {
|
|
kioconnection.disconnect();
|
|
connected = false;
|
|
}
|
|
public InputStream getInputStream() throws IOException {
|
|
doInput = true;
|
|
doOutput = false;
|
|
connect();
|
|
return kioconnection.getInputStream();
|
|
}
|
|
public OutputStream getOutputStream() throws IOException {
|
|
doInput = false;
|
|
doOutput = true;
|
|
connect();
|
|
return kioconnection.getOutputStream();
|
|
}
|
|
public InputStream getErrorStream() {
|
|
Main.debug("KIO KJASHttpURLConnection.getErrorStream" + url);
|
|
try {
|
|
if (connected && kioconnection.responseCode == 404)
|
|
return kioconnection.getInputStream();
|
|
} catch (Exception ex) {}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
final class KJASSimpleURLConnection extends URLConnection
|
|
{
|
|
private KIOSimpleConnection kioconnection = null;
|
|
private int default_port;
|
|
|
|
KJASSimpleURLConnection(URL u, int p) {
|
|
super(u);
|
|
default_port = p;
|
|
}
|
|
public boolean usingProxy() {
|
|
return false; // FIXME
|
|
}
|
|
public Permission getPermission() throws IOException {
|
|
int p = url.getPort();
|
|
if (p < 0)
|
|
p = default_port;
|
|
return new SocketPermission(url.getHost() + ":" + p, "connect");
|
|
}
|
|
public void connect() throws IOException {
|
|
if (kioconnection != null)
|
|
return;
|
|
Main.debug ("KIO KJASSimpleURLConnection.connection " + url);
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null)
|
|
security.checkPermission(getPermission());
|
|
kioconnection = new KIOSimpleConnection(url);
|
|
kioconnection.connect(doInput);
|
|
connected = true;
|
|
}
|
|
public void disconnect() {
|
|
if (kioconnection == null)
|
|
return;
|
|
kioconnection.disconnect();
|
|
kioconnection = null;
|
|
connected = false;
|
|
}
|
|
public InputStream getInputStream() throws IOException {
|
|
doInput = true;
|
|
doOutput = false;
|
|
if (kioconnection == null)
|
|
connect();
|
|
return kioconnection.getInputStream();
|
|
}
|
|
public OutputStream getOutputStream() throws IOException {
|
|
doInput = false;
|
|
doOutput = true;
|
|
if (kioconnection == null)
|
|
connect();
|
|
return kioconnection.getOutputStream();
|
|
}
|
|
}
|
|
|
|
|
|
final class KJASHttpURLStreamHandler extends URLStreamHandler
|
|
{
|
|
KJASHttpURLStreamHandler(int port) {
|
|
default_port = port;
|
|
}
|
|
protected URLConnection openConnection(URL u) throws IOException {
|
|
URL url = new URL(u.toExternalForm());
|
|
return new KJASHttpURLConnection(url);
|
|
}
|
|
protected int getDefaultPort() {
|
|
return default_port;
|
|
}
|
|
private int default_port;
|
|
}
|
|
|
|
final class KJASSimpleURLStreamHandler extends URLStreamHandler
|
|
{
|
|
KJASSimpleURLStreamHandler(int port) {
|
|
default_port = port;
|
|
}
|
|
protected URLConnection openConnection(URL u) throws IOException {
|
|
URL url = new URL(u.toExternalForm());
|
|
return new KJASSimpleURLConnection(url, default_port);
|
|
}
|
|
protected int getDefaultPort() {
|
|
return default_port;
|
|
}
|
|
private int default_port;
|
|
}
|
|
|
|
public final class KJASURLStreamHandlerFactory
|
|
implements URLStreamHandlerFactory
|
|
{
|
|
public URLStreamHandler createURLStreamHandler(String protocol) {
|
|
if (protocol.equals("jar") || protocol.equals("file"))
|
|
return null;
|
|
//outputs to early: Main.debug ("createURLStreamHandler " + protocol);
|
|
Main.debug ("KIO createURLStreamHandler " + protocol);
|
|
if (protocol.equals("http"))
|
|
return new KJASHttpURLStreamHandler(80);
|
|
else if (protocol.equals("https"))
|
|
return new KJASHttpURLStreamHandler(443);
|
|
else if (protocol.equals("ftp"))
|
|
return new KJASSimpleURLStreamHandler(21);
|
|
else if (protocol.equals("smb"))
|
|
return new KJASSimpleURLStreamHandler(139);
|
|
else if (protocol.equals("fish"))
|
|
return new KJASSimpleURLStreamHandler(22);
|
|
return null;
|
|
}
|
|
}
|