2002-12-18 19:00:25 +03:00
|
|
|
/*
|
2005-05-23 12:24:07 +04:00
|
|
|
Copyright (C) 2002, 2003, 2004, 2005 Jeroen Frijters
|
2002-12-18 19:00:25 +03:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
|
|
claim that you wrote the original software. If you use this software
|
|
|
|
in a product, an acknowledgment in the product documentation would be
|
|
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
|
|
|
|
Jeroen Frijters
|
|
|
|
jeroen@frijters.net
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
package gnu.java.net.protocol.ikvmres;
|
|
|
|
|
2004-03-16 20:10:09 +03:00
|
|
|
import cli.System.Resources.*;
|
2003-08-21 14:06:34 +04:00
|
|
|
import cli.System.Reflection.*;
|
2004-03-16 20:10:09 +03:00
|
|
|
import cli.System.Collections.*;
|
2002-12-18 19:00:25 +03:00
|
|
|
import java.net.*;
|
|
|
|
import java.io.*;
|
|
|
|
import java.io.IOException;
|
|
|
|
|
2005-05-23 12:24:07 +04:00
|
|
|
class LZInputStream extends FilterInputStream
|
|
|
|
{
|
|
|
|
private int[] ptr_tbl;
|
|
|
|
private int[] char_tbl;
|
|
|
|
private int[] stack;
|
|
|
|
private int table_size;
|
|
|
|
private int count;
|
|
|
|
private int bitoff;
|
|
|
|
private int bitbuf;
|
|
|
|
private int prev = -1;
|
|
|
|
private int bits;
|
|
|
|
private int cc;
|
|
|
|
private int fc;
|
|
|
|
private int sp;
|
|
|
|
|
|
|
|
public LZInputStream(InputStream in) throws IOException
|
|
|
|
{
|
|
|
|
super(in);
|
|
|
|
bitoff = 0;
|
|
|
|
count = 0;
|
|
|
|
table_size = 256;
|
|
|
|
bits = 9;
|
|
|
|
ptr_tbl = new int[table_size];
|
|
|
|
char_tbl = new int[table_size];
|
|
|
|
stack = new int[table_size];
|
|
|
|
sp = 0;
|
|
|
|
cc = prev = incode();
|
|
|
|
stack[sp++] = cc;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int read() throws IOException
|
|
|
|
{
|
|
|
|
if (sp == 0)
|
|
|
|
{
|
|
|
|
if (stack.length != table_size)
|
|
|
|
{
|
|
|
|
stack = new int[table_size];
|
|
|
|
}
|
|
|
|
int ic = cc = incode();
|
|
|
|
if (cc == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (count >= 0 && cc >= count + 256)
|
|
|
|
{
|
|
|
|
stack[sp++] = fc;
|
|
|
|
cc = prev;
|
|
|
|
ic = find(prev, fc);
|
|
|
|
}
|
|
|
|
while (cc >= 256)
|
|
|
|
{
|
|
|
|
stack[sp++] = char_tbl[cc - 256];
|
|
|
|
cc = ptr_tbl[cc - 256];
|
|
|
|
}
|
|
|
|
fc = stack[sp++] = cc;
|
|
|
|
if (count >= 0)
|
|
|
|
{
|
|
|
|
ptr_tbl[count] = prev;
|
|
|
|
char_tbl[count] = fc;
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
if (count == table_size)
|
|
|
|
{
|
|
|
|
count = -1;
|
|
|
|
if (bits == 12)
|
|
|
|
{
|
|
|
|
table_size = 256;
|
|
|
|
bits = 9;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bits++;
|
|
|
|
table_size = (1 << bits) - 256;
|
|
|
|
}
|
|
|
|
ptr_tbl = null;
|
|
|
|
char_tbl = null;
|
|
|
|
ptr_tbl = new int[table_size];
|
|
|
|
char_tbl= new int[table_size];
|
|
|
|
}
|
|
|
|
prev = ic;
|
|
|
|
}
|
|
|
|
return stack[--sp] & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int find(int p, int c)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
if (ptr_tbl[i] == p && char_tbl[i] == c)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return i + 256;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int incode() throws IOException
|
|
|
|
{
|
|
|
|
while (bitoff < bits)
|
|
|
|
{
|
|
|
|
int v = in.read();
|
|
|
|
if (v == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
bitbuf |= (v & 0xFF) << bitoff;
|
|
|
|
bitoff += 8;
|
|
|
|
}
|
|
|
|
bitoff -= bits;
|
|
|
|
int result = bitbuf;
|
|
|
|
bitbuf >>= bits;
|
|
|
|
result -= bitbuf << bits;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int read(byte[] b) throws IOException
|
|
|
|
{
|
|
|
|
return read(b, 0, b.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int read(byte[] b, int off, int len) throws IOException
|
|
|
|
{
|
|
|
|
if(len == 0)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int i = 0;
|
|
|
|
for (; i < len ; i++)
|
|
|
|
{
|
|
|
|
int r = read();
|
|
|
|
if(r == -1)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
b[off + i] = (byte)r;
|
|
|
|
}
|
|
|
|
return (i == 0) ? -1 : i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-18 19:00:25 +03:00
|
|
|
class IkvmresURLConnection extends URLConnection
|
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
private InputStream inputStream;
|
2002-12-18 19:00:25 +03:00
|
|
|
|
2004-03-16 20:10:09 +03:00
|
|
|
IkvmresURLConnection(URL url)
|
|
|
|
{
|
|
|
|
super(url);
|
|
|
|
doOutput = false;
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
|
2004-09-05 13:37:58 +04:00
|
|
|
private static native String MangleResourceName(String name);
|
|
|
|
|
2004-03-16 20:10:09 +03:00
|
|
|
public void connect() throws IOException
|
|
|
|
{
|
|
|
|
if(!connected)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
String assembly = url.getHost();
|
|
|
|
String resource = url.getFile();
|
|
|
|
if(assembly == null || resource == null || !resource.startsWith("/"))
|
|
|
|
{
|
2004-12-21 13:26:51 +03:00
|
|
|
throw new MalformedURLException(url.toString());
|
2004-03-16 20:10:09 +03:00
|
|
|
}
|
|
|
|
resource = resource.substring(1);
|
2004-12-21 13:26:51 +03:00
|
|
|
cli.System.IO.Stream s;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if(false) throw new cli.System.IO.FileNotFoundException();
|
|
|
|
if(false) throw new cli.System.BadImageFormatException();
|
|
|
|
if(false) throw new cli.System.Security.SecurityException();
|
|
|
|
Assembly asm = Assembly.Load(assembly);
|
|
|
|
s = asm.GetManifestResourceStream(MangleResourceName(resource));
|
|
|
|
if(s == null)
|
|
|
|
{
|
|
|
|
throw new FileNotFoundException("resource " + resource + " not found in assembly " + assembly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(cli.System.IO.FileNotFoundException x)
|
|
|
|
{
|
|
|
|
throw (IOException)new FileNotFoundException(assembly).initCause(x);
|
|
|
|
}
|
|
|
|
catch(cli.System.BadImageFormatException x1)
|
|
|
|
{
|
|
|
|
throw (IOException)new IOException().initCause(x1);
|
|
|
|
}
|
|
|
|
catch(cli.System.Security.SecurityException x2)
|
|
|
|
{
|
|
|
|
throw (IOException)new IOException().initCause(x2);
|
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
try
|
|
|
|
{
|
2005-02-02 18:11:26 +03:00
|
|
|
Object r;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
r = ResourceReader.class.getConstructor(new Class[] { cli.System.IO.Stream.class }).newInstance(new Object[] { s });
|
|
|
|
}
|
|
|
|
catch(Exception x)
|
|
|
|
{
|
|
|
|
throw (IOException)new IOException().initCause(x);
|
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
try
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2005-02-02 18:11:26 +03:00
|
|
|
IEnumerator e = ((IEnumerable)r).GetEnumerator();
|
2004-03-16 20:10:09 +03:00
|
|
|
if(!e.MoveNext())
|
|
|
|
{
|
2005-05-23 12:24:07 +04:00
|
|
|
throw new IOException("Invalid resource " + resource + " found in assembly " + assembly);
|
2004-03-16 20:10:09 +03:00
|
|
|
}
|
2005-05-23 12:24:07 +04:00
|
|
|
DictionaryEntry de = (DictionaryEntry)e.get_Current();
|
|
|
|
String key = (String)de.get_Key();
|
|
|
|
byte[] value = (byte[])de.get_Value();
|
|
|
|
inputStream = new ByteArrayInputStream(value);
|
|
|
|
if(key.equals("lz"))
|
|
|
|
{
|
|
|
|
inputStream = new LZInputStream(inputStream);
|
|
|
|
}
|
|
|
|
else if(key.equals("ikvm"))
|
|
|
|
{
|
|
|
|
// not compressed
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new IOException("Unsupported resource encoding " + key + " for resource " + resource + " found in assembly " + assembly);
|
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
connected = true;
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
finally
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2005-02-02 18:11:26 +03:00
|
|
|
((cli.System.IDisposable)r).Dispose();
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
s.Close();
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
|
2004-03-16 20:10:09 +03:00
|
|
|
public InputStream getInputStream() throws IOException
|
|
|
|
{
|
|
|
|
if(!connected)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
connect();
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
return inputStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
public OutputStream getOutputStream() throws IOException
|
|
|
|
{
|
|
|
|
throw new IOException("resource URLs are read only");
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getLastModified()
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getContentLength()
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
|
2004-03-16 20:10:09 +03:00
|
|
|
public class Handler extends URLStreamHandler
|
|
|
|
{
|
|
|
|
private static final String RFC2396_DIGIT = "0123456789";
|
|
|
|
private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
private static final String RFC2396_UPALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
private static final String RFC2396_ALPHA = RFC2396_LOWALPHA + RFC2396_UPALPHA;
|
|
|
|
private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA;
|
|
|
|
private static final String RFC2396_MARK = "-_.!~*'()";
|
|
|
|
private static final String RFC2396_UNRESERVED = RFC2396_ALPHANUM + RFC2396_MARK;
|
|
|
|
private static final String RFC2396_REG_NAME = RFC2396_UNRESERVED + "$,;:@&=+";
|
|
|
|
private static final String RFC2396_PCHAR = RFC2396_UNRESERVED + ":@&=+$,";
|
|
|
|
private static final String RFC2396_SEGMENT = RFC2396_PCHAR + ";";
|
|
|
|
private static final String RFC2396_PATH_SEGMENTS = RFC2396_SEGMENT + "/";
|
|
|
|
|
|
|
|
protected URLConnection openConnection(URL url) throws IOException
|
|
|
|
{
|
|
|
|
return new IkvmresURLConnection(url);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void parseURL(URL url, String url_string, int start, int end)
|
|
|
|
{
|
|
|
|
try
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
// NOTE originally I wanted to use java.net.URI to handling parsing and constructing of these things,
|
|
|
|
// but it turns out that URI uses regex and that depends on resource loading...
|
|
|
|
url_string = url_string.substring(start, end);
|
2004-12-21 13:26:51 +03:00
|
|
|
if(url_string.startsWith("//"))
|
2004-03-16 20:10:09 +03:00
|
|
|
{
|
2004-12-21 13:26:51 +03:00
|
|
|
int slash = url_string.indexOf('/', 2);
|
|
|
|
if(slash == -1)
|
|
|
|
{
|
|
|
|
throw new gnu.java.net.URLParseError("ikvmres: URLs must contain path");
|
|
|
|
}
|
|
|
|
String assembly = unquote(url_string.substring(2, slash));
|
|
|
|
String file = unquote(url_string.substring(slash));
|
|
|
|
setURL(url, "ikvmres", assembly, -1, file, null);
|
|
|
|
}
|
|
|
|
else if(url_string.startsWith("/"))
|
|
|
|
{
|
|
|
|
setURL(url, "ikvmres", url.getHost(), -1, url_string, null);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
String[] baseparts = ((cli.System.String)(Object)url.getFile()).Split(new char[] { '/' });
|
|
|
|
String[] relparts = ((cli.System.String)(Object)url_string).Split(new char[] { '/' });
|
|
|
|
String[] target = new String[baseparts.length + relparts.length - 1];
|
|
|
|
for(int i = 1; i < baseparts.length; i++)
|
|
|
|
{
|
|
|
|
target[i - 1] = baseparts[i];
|
|
|
|
}
|
|
|
|
int p = baseparts.length - 2;
|
|
|
|
for(int i = 0; i < relparts.length; i++)
|
|
|
|
{
|
|
|
|
if(relparts[i].equals("."))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else if(relparts[i].equals(".."))
|
|
|
|
{
|
|
|
|
p = Math.max(0, p - 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
target[p++] = relparts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
StringBuffer file = new StringBuffer();
|
|
|
|
for(int i = 0; i < p; i++)
|
|
|
|
{
|
|
|
|
file.append('/').append(target[i]);
|
|
|
|
}
|
|
|
|
setURL(url, "ikvmres", url.getHost(), -1, file.toString(), null);
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
catch(URISyntaxException x)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
throw new gnu.java.net.URLParseError(x.getMessage());
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
protected String toExternalForm(URL url)
|
|
|
|
{
|
2004-07-10 11:19:42 +04:00
|
|
|
// NOTE originally I wanted to use java.net.URI to handle parsing and constructing of these things,
|
2004-03-16 20:10:09 +03:00
|
|
|
// but it turns out that URI uses regex and that depends on resource loading...
|
|
|
|
return "ikvmres://" + quote(url.getHost(), RFC2396_REG_NAME) + quote(url.getFile(), RFC2396_PATH_SEGMENTS);
|
|
|
|
}
|
|
|
|
|
2004-07-10 11:19:42 +04:00
|
|
|
protected InetAddress getHostAddress(URL url)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean hostsEqual(URL url1, URL url2)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2004-03-16 20:10:09 +03:00
|
|
|
private static String quote (String str, String legalCharacters)
|
|
|
|
{
|
|
|
|
StringBuffer sb = new StringBuffer(str.length());
|
|
|
|
for (int i = 0; i < str.length(); i++)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
char c = str.charAt(i);
|
|
|
|
if (legalCharacters.indexOf(c) == -1)
|
|
|
|
{
|
|
|
|
String hex = "0123456789ABCDEF";
|
|
|
|
if (c <= 127)
|
|
|
|
{
|
|
|
|
sb.append('%')
|
|
|
|
.append(hex.charAt(c / 16))
|
|
|
|
.append(hex.charAt(c % 16));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// this is far from optimal, but it works
|
|
|
|
byte[] utf8 = str.substring(i, i + 1).getBytes("utf-8");
|
|
|
|
for (int j = 0; j < utf8.length; j++)
|
|
|
|
{
|
|
|
|
sb.append('%')
|
|
|
|
.append(hex.charAt((utf8[j] & 0xff) / 16))
|
|
|
|
.append(hex.charAt((utf8[j] & 0xff) % 16));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (java.io.UnsupportedEncodingException x)
|
|
|
|
{
|
|
|
|
throw (Error)new InternalError().initCause(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sb.append(c);
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
return sb.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String unquote (String str)
|
|
|
|
throws URISyntaxException
|
|
|
|
{
|
|
|
|
if (str == null)
|
|
|
|
return null;
|
|
|
|
byte[] buf = new byte[str.length()];
|
|
|
|
int pos = 0;
|
|
|
|
for (int i = 0; i < str.length(); i++)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
char c = str.charAt(i);
|
|
|
|
if (c > 127)
|
|
|
|
throw new URISyntaxException(str, "Invalid character");
|
|
|
|
if (c == '%')
|
|
|
|
{
|
|
|
|
if (i + 2 >= str.length())
|
|
|
|
throw new URISyntaxException(str, "Invalid quoted character");
|
|
|
|
String hex = "0123456789ABCDEF";
|
|
|
|
int hi = hex.indexOf(str.charAt(++i));
|
|
|
|
int lo = hex.indexOf(str.charAt(++i));
|
|
|
|
if (lo < 0 || hi < 0)
|
|
|
|
throw new URISyntaxException(str, "Invalid quoted character");
|
|
|
|
buf[pos++] = (byte)(hi * 16 + lo);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf[pos++] = (byte)c;
|
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
try
|
|
|
|
{
|
|
|
|
return new String(buf, 0, pos, "utf-8");
|
|
|
|
}
|
|
|
|
catch (java.io.UnsupportedEncodingException x2)
|
2002-12-18 19:00:25 +03:00
|
|
|
{
|
2004-03-16 20:10:09 +03:00
|
|
|
throw (Error)new InternalError().initCause(x2);
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|
2004-03-16 20:10:09 +03:00
|
|
|
}
|
2002-12-18 19:00:25 +03:00
|
|
|
}
|