2002/05/04 07:25:32
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:46:53 +0000 (06:46 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 06:46:53 +0000 (06:46 +0000)
darcs-hash:20040130064653-2ba56-c9eb360d9e3f46e3a051908a72c0958571da7b90.gz

CHANGES
src/org/xwt/HTTP.java [new file with mode: 0644]
src/org/xwt/Main.java
src/org/xwt/Platform.java
src/org/xwt/SOAP.java
src/org/xwt/XMLRPC.java
src/org/xwt/plat/Java12.java
src/org/xwt/plat/Java12.xml
src/org/xwt/plat/Win32.cc
src/org/xwt/plat/Win32.java

diff --git a/CHANGES b/CHANGES
index c00ac4e..063e6b7 100644 (file)
--- a/CHANGES
+++ b/CHANGES
 
 03-Apr megacz Main.java: fixed a bug that could cause >100% instantiation
 
+03-Apr megacz HTTP.java, Main.java, Platform.java, SOAP.java,
+              XMLRPC.java, Java12.java, Win32.cc, Win32.java: new HTTP
+              architecture, first implementation of proxy support.
+
 
 
 
diff --git a/src/org/xwt/HTTP.java b/src/org/xwt/HTTP.java
new file mode 100644 (file)
index 0000000..075a641
--- /dev/null
@@ -0,0 +1,462 @@
+// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import org.xwt.util.*;
+import org.mozilla.javascript.*;
+
+/**
+ *  A crude HTTP[S] connection implementation with support for proxies, since not all Java libraries
+ *  (particularly GCJ's) support proxies.
+ *
+ *  FEATURE: implement pipelining
+ */
+public class HTTP {
+
+    /** the URL to connect to */
+    URL url = null;
+
+    /** the host to connect to */
+    String host = null;
+
+    /** the port to connect on */
+    int port = -1;
+
+    /** true if SSL (HTTPS) should be used */
+    boolean ssl = false;
+
+    /** the path (URI) to retrieve on the server */
+    String path = null;
+
+    /** the socket; created lazily */
+    Socket sock = null;
+
+    /** the socket's inputstream */
+    InputStream in = null;
+
+    /** the socket's outputstream */
+    OutputStream out = null;
+
+    /** the content-type of the data being received */
+    String contentType = null;
+
+    /** the content-length of the data being recieved */
+    int contentLength = 0;
+
+    /** true iff a proxy should be used */
+    boolean proxy = false;
+
+    /** additional headers to be transmitted */
+    String headers = "";
+
+    public HTTP(String url) throws MalformedURLException, IOException {
+        if (url.startsWith("https:")) {
+            url = "http" + url.substring(5);
+            ssl = true;
+        }
+        if (!url.startsWith("http:")) throw new IOException("HTTP only supports http/https urls");
+        this.url = new URL(url);
+        host = this.url.getHost();
+        port = this.url.getPort();
+        path = this.url.getFile();
+        if (port == -1) port = ssl ? 443 : 80;
+        try {
+            InetAddress addr = InetAddress.getByName(host);
+            byte[] quadbyte = addr.getAddress();
+            if (quadbyte[0] == 10 || (quadbyte[0] == 192 && quadbyte[1] == 168) ||
+                (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16) && !addr.equals(Main.originAddr))
+                throw new IOException("security violation: " + host + " [" + addr.getHostAddress() + "] is in a firewalled netblock");
+        } catch (UnknownHostException uhe) {
+            if (Platform.detectProxy() == null) throw new IOException("could not resolve hostname \"" + host + "\" and no proxy configured");
+            else if (Log.on) Log.log("could not resolve host " + host + "; assuming that the proxy can resolve it for us");
+        }
+    }
+
+    public String getContentType() throws IOException {
+        getInputStream();
+        return contentType;
+    }
+
+    public int getContentLength() throws IOException {
+        getInputStream();
+        return contentLength;
+    }
+
+    public void addHeader(String header, String value) throws IOException {
+        if (in != null) throw new IOException("attempt to add header after connection has been made");
+        headers += header + ": " + value + "\r\n";
+    }
+
+    private void getSock() throws IOException {
+        ProxyInfo pi = Platform.detectProxy();
+
+        // unproxied
+        if (pi == null || (pi.proxyAutoConfigFunction == null && pi.socksProxyHost == null && pi.httpProxyHost == null)) {
+            if (Log.on) Log.log(this, "creating unproxied socket to " + host + ":" + port + (ssl ? " [ssl]" : ""));
+            sock = Platform.getSocket(host, port, ssl);
+            return;
+        }
+
+        // no PAC; simple config
+        if (pi.proxyAutoConfigFunction == null) {
+            String proxyHost = ssl && pi.httpsProxyHost != null ? pi.httpsProxyHost : pi.httpProxyHost;
+            int proxyPort = ssl && pi.httpsProxyHost != null ? pi.httpsProxyPort : pi.httpProxyPort;
+            if (Log.on) Log.log(this, "no proxyAutoConfigFunction; using proxy " + proxyHost + ":" + proxyPort);
+            sock = Platform.getSocket(proxyHost, proxyPort, ssl);
+            proxy = true;
+            return;
+        }
+        
+        // PAC
+        String pac = null;
+        try {
+            Context cx = Context.enter();
+            Object obj = pi.proxyAutoConfigFunction.call(cx, ProxyInfo.base, null, new Object[] { url.toString(), host });
+            if (Log.on) Log.log(this, "PAC script returned \"" + obj + "\"");
+            pac = obj.toString();
+        } catch (Throwable e) {
+            if (Log.on) Log.log(this, "PAC script threw an exception:");
+            if (Log.on) Log.log(this, e);
+            throw new IOException("PAC script threw exception " + e);
+        }
+
+        StringTokenizer st = new StringTokenizer(pac, ";", false);
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken().trim();
+            if (Log.on) Log.log(this, "  trying \"" + token + "\"...");
+            try {
+                if (token.startsWith("DIRECT")) {
+                    proxy = false;
+                    sock = Platform.getSocket(host, port, ssl);
+                    return;
+                } else if (token.startsWith("PROXY")) { 
+                    proxy = true;
+                    sock = Platform.getSocket(token.substring(token.indexOf(' ') + 1, token.indexOf(':')),
+                                              Integer.parseInt(token.substring(token.indexOf(':') + 1)), ssl);
+                    return;
+                } else if (token.startsWith("SOCKS")) {
+                    // FIXME
+                }
+            } catch (Throwable e) {
+                if (Log.on) Log.log(this, "attempt at \"" + proxy + "\" failed due to " + e + "; trying next one");
+            }
+        }
+        throw new IOException("all proxy options exhausted");
+    }
+
+    public OutputStream getOutputStream(int contentLength, String contentType) throws IOException {
+        if (out != null) return out;
+        if (in != null) throw new IOException("attempt to getOutputStream() after getInputStream()");
+        getSock();
+        sock.setTcpNoDelay(true);
+        out = sock.getOutputStream();
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
+        pw.print("POST " + (proxy ? url.toString() : path) + " HTTP/1.0\r\n");
+        pw.print("Host: " + host + "\r\n");
+        pw.print("User-Agent: XWT\r\n");
+        pw.print("Content-length: " + contentLength + "\r\n");
+        pw.print(headers);
+        if (contentType != null) pw.print("Content-Type: " + contentType + "\r\n");
+        pw.print("\r\n");
+        pw.flush();
+        return out;
+    }
+
+    public InputStream getInputStream() throws IOException {
+        if (in != null) return in;
+        if (out != null) {
+            out.flush();
+        } else {
+            getSock();
+            sock.setTcpNoDelay(true);
+            PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));
+            pw.print("GET " + (proxy ? url.toString() : path) + " HTTP/1.0\r\n");
+            pw.print("Host: " + host + "\r\n");
+            pw.print("User-Agent: XWT\r\n");
+            pw.print(headers);
+            pw.print("\r\n");
+            pw.flush();
+        }
+
+        in = new BufferedInputStream(sock.getInputStream());
+
+        // we can't use a BufferedReader directly on the input stream,
+        // since it will buffer beyond the end of the headers
+        byte[] buf = new byte[4096];
+        int buflen = 0;
+        while(true) {
+            int read = in.read();
+            if (read == -1) throw new IOException("stream closed while reading headers");
+            buf[buflen++] = (byte)read;
+            if (buflen >= 4 && buf[buflen - 4] == '\r' && buf[buflen - 3] == '\n' && buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n') break;
+            if (buflen == buf.length) {
+                byte[] newbuf = new byte[buf.length * 2];
+                System.arraycopy(buf, 0, newbuf, 0, buflen);
+                buf = newbuf;
+            }
+        }
+        
+        BufferedReader headerReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, buflen)));
+        String s = headerReader.readLine();
+        if (!s.startsWith("HTTP/")) throw new IOException("Expected reply to start with \"HTTP/\"");
+        String reply = s.substring(s.indexOf(' ') + 1);
+        if (!reply.startsWith("2")) throw new IOException("HTTP Error: " + reply);
+        while((s = headerReader.readLine()) != null) {
+            if (s.length() > 15 && s.substring(0, 15).equalsIgnoreCase("content-length: "))
+                contentLength = Integer.parseInt(s.substring(15));
+        }
+
+        return in;
+    }
+
+    public static class ProxyInfo {
+
+        public ProxyInfo() { }
+
+        /** the HTTP Proxy host to use */
+        public String httpProxyHost = null;
+
+        /** the HTTP Proxy port to use */
+        public int httpProxyPort = -1;
+
+        /** if a seperate proxy should be used for HTTPS, this is the hostname; otherwise, httpProxyHost is used */
+        public String httpsProxyHost = null;
+
+        /** if a seperate proxy should be used for HTTPS, this is the port */
+        public int httpsProxyPort = -1;
+
+        /** the SOCKS Proxy Host to use */
+        public String socksProxyHost = null;
+
+        /** the SOCKS Proxy Port to use */
+        public int socksProxyPort = -1;
+
+        /** hosts to be excluded from proxy use; wildcards permitted */
+        public String[] excluded = null;
+
+        /** the PAC script */
+        public Function proxyAutoConfigFunction = null;
+
+        public static ProxyInfo detectProxyViaManual() {
+            try {
+                try { InetAddress.getByName("xwt-proxy-httpHost");
+                } catch (UnkownHostException unhe) { InetAddress.getByName("xwt-proxy-socksHost"); }
+
+                if (Log.on) Log.log(Platform.class, "using xwt-proxy-* configuration");
+                ProxyInfo ret = new ProxyInfo();
+                try {
+                    ret.httpProxyHost = InetAddress.getByName("xwt-proxy-httpHost").getHostAddress();
+                    byte[] quadbyte = InetAddress.getByName("xwt-proxy-httpPort").getAddress();
+                    ret.httpProxyPort = ((quadbyte[1] & 0xff) * 10000) + ((quadbyte[2] & 0xff) * 100) + (quadbyte[3] & 0xff);
+                } catch (UnknownHostException e) { }
+                try {
+                    ret.httpsProxyHost = InetAddress.getByName("xwt-proxy-httpsHost").getHostAddress();
+                    byte[] quadbyte = InetAddress.getByName("xwt-proxy-httpsPort").getAddress();
+                    ret.httpsProxyPort = ((quadbyte[1] & 0xff) * 10000) + ((quadbyte[2] & 0xff) * 100) + (quadbyte[3] & 0xff);
+                } catch (UnknownHostException e) { }
+                try {
+                    ret.socksProxyHost = InetAddress.getByName("xwt-proxy-socksHost").getHostAddress();
+                    byte[] quadbyte = InetAddress.getByName("xwt-proxy-socksPort").getAddress();
+                    ret.socksProxyPort = ((quadbyte[1] & 0xff) * 10000) + ((quadbyte[2] & 0xff) * 100) + (quadbyte[3] & 0xff);
+                } catch (UnknownHostException e) { }
+                return ret;
+            } catch (UnknownHostException e) {
+                if (Log.on) Log.log(Platform.class, "xwt-proxy-* detection failed due to:");
+                return null;
+            }
+        }
+
+        // FIXME: search up from default domain
+        public static ProxyInfo detectProxyViaWPAD() {
+            try {
+                InetAddress wpad = InetAddress.getByName("wpad");
+                if (Log.on) Log.log(Platform.class, "using Web Proxy Auto Detection to detect proxy settings");
+                ProxyInfo ret = new ProxyInfo();
+                ret.proxyAutoConfigFunction = getProxyAutoConfigFunction("http://wpad/wpad.dat");
+                if (ret.proxyAutoConfigFunction != null) return ret;
+            } catch (UnknownHostException e) {
+                if (Log.on) Log.log(HTTP.class, "couldn't find WPAD server: " + e);
+            }
+            return null;
+        }
+
+        public static Scriptable proxyAutoConfigRootScope = new ProxyAutoConfigRootScope();
+
+        public static Function getProxyAutoConfigFunction(String url) {
+            try { 
+                Context cx = Context.enter();
+                cx.setOptimizationLevel(-1);
+                BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url).getInputStream()));
+                String s = null;
+                String script = "";
+                while((s = br.readLine()) != null) script += s + "\n";
+                if (Log.on) Log.log(HTTP.ProxyInfo.class, "successfully retrieved WPAD PAC:");
+                if (Log.on) Log.log(HTTP.ProxyInfo.class, script);
+                Script scr = cx.compileReader(proxyAutoConfigRootScope, new StringReader(script), "PAC script at " + url, 0, null);
+                scr.exec(cx, proxyAutoConfigRootScope);
+                return (Function)proxyAutoConfigRootScope.get("FindProxyForURL", null);
+            } catch (Exception e) {
+                if (Log.on) {
+                    Log.log(Platform.class, "WPAD detection failed due to:");
+                    if (e instanceof EcmaError) Log.log(HTTP.class, ((EcmaError)e).getMessage() + " at " +
+                                                        ((EcmaError)e).getSourceName() + ":" + ((EcmaError)e).getLineNumber());
+                    else Log.log(Platform.class, e);
+                }
+                return null;
+            }
+        }
+
+        public static class ProxyAutoConfigRootScope extends ScriptableObject {
+
+            public String getClassName() { return "ProxyAutoConfigRootScope"; }
+            ProxyAutoConfigRootScope() { Context.enter().initStandardObjects(this); }
+
+            public Object get(String name, Scriptable start) {
+                if (name.equals("isPlainHostName")) return isPlainHostName;
+                else if (name.equals("dnsDomainIs")) return dnsDomainIs;
+                else if (name.equals("localHostOrDomainIs")) return localHostOrDomainIs;
+                else if (name.equals("isResolvable")) return isResolvable;
+                else if (name.equals("dnsResolve")) return dnsResolve;
+                else if (name.equals("myIpAddress")) return myIpAddress;
+                else if (name.equals("dnsDomainLevels")) return dnsDomainLevels;
+                else if (name.equals("shExpMatch")) return shExpMatch;
+                else if (name.equals("weekdayRange")) return weekdayRange;
+                else if (name.equals("dateRange")) return dateRange;
+                else if (name.equals("timeRange")) return timeRange;
+                else if (name.equals("ProxyConfig")) return ProxyConfig;
+                else return super.get(name, start);
+            }
+
+            private static final JSObject proxyConfigBindings = new JSObject();
+            private static final JSObject ProxyConfig = new JSObject() {
+                    public Object get(String name, Scriptable start) {
+                        if (name.equals("bindings")) return proxyConfigBindings;
+                        return null;
+                    }
+                };
+            
+            private static abstract class JSFunction extends JSObject implements Function {
+                JSFunction() { setSeal(true); }
+                public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) { return null; }
+            }
+
+            private static final JSFunction isPlainHostName = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        return (args[0].toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE;
+                    }
+                };
+
+            private static final JSFunction dnsDomainIs = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        return (args[0].toString().endsWith(args[1].toString())) ? Boolean.TRUE : Boolean.FALSE;
+                    }
+                };
+            
+            private static final JSFunction localHostOrDomainIs = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        return (args[0].toString().equals(args[1].toString()) || 
+                                (args[0].toString().indexOf('.') == -1 && args[1].toString().startsWith(args[0].toString()))) ?
+                            Boolean.TRUE : Boolean.FALSE;
+                    }
+                };
+            
+            private static final JSFunction isResolvable = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        try {
+                            return (InetAddress.getByName(args[0].toString()) != null) ? Boolean.TRUE : Boolean.FALSE;
+                        } catch (UnknownHostException e) {
+                            return Boolean.FALSE;
+                        }
+                    }
+                };
+
+            private static final JSFunction isInNet = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        // FIXME
+                        return null;
+                    }
+                };
+
+            private static final JSFunction dnsResolve = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        try {
+                            return InetAddress.getByName(args[0].toString()).getHostAddress();
+                        } catch (UnknownHostException e) {
+                            return null;
+                        }
+                    }
+                };
+
+            private static final JSFunction myIpAddress = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        try {
+                            return InetAddress.getLocalHost().getHostAddress();
+                        } catch (UnknownHostException e) {
+                            if (Log.on) Log.log(this, "strange... host does not know its own address");
+                            return null;
+                        }
+                    }
+                };
+
+            private static final JSFunction dnsDomainLevels = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        String s = args[0].toString();
+                        int i = 0;
+                        while((i = s.indexOf('.', i)) != -1) i++;
+                        return new Integer(i);
+                    }
+                };
+        
+            // FIXME: test this!
+            private static boolean match(String[] arr, String s, int index) {
+                if (index == arr.length) return true;
+                for(int i=0; i<s.length(); i++) {
+                    String s2 = s.substring(i);
+                    if (s2.startsWith(arr[index]) && match(arr, s.substring(arr[index].length()), index + 1)) return true;
+                }
+                return false;
+            }
+
+            private static final JSFunction shExpMatch = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        StringTokenizer st = new StringTokenizer(args[1].toString(), "*", false);
+                        String[] arr = new String[st.countTokens()];
+                        String s = args[0].toString();
+                        for (int i=0; st.hasMoreTokens(); i++) arr[i] = st.nextToken();
+                        return match(arr, s, 0) ? Boolean.TRUE : Boolean.FALSE;
+                    }
+                };
+
+            private static final JSFunction weekdayRange = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        throw new JavaScriptException("XWT does not support weekdayRange() in PAC scripts");
+                        /*
+                        TimeZone tz = (args.length < 3 || args[2] == null || !args[2].equals("GMT")) ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
+                        Calendar c = new Calendar();
+                        c.setTimeZone(tz);
+                        c.setTime(new Date());
+                        Date d = c.getTime();
+                        if (args.length == 1) return 
+                        */
+                    }
+                };
+
+            private static final JSFunction dateRange = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        throw new JavaScriptException("XWT does not support dateRange() in PAC scripts");
+                    }
+                };
+
+            private static final JSFunction timeRange = new JSFunction() {
+                    public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
+                        throw new JavaScriptException("XWT does not support timeRange() in PAC scripts");
+                    }
+                };
+
+        }
+
+    }
+
+}
index 0bcf0f4..2ac536d 100644 (file)
@@ -90,8 +90,12 @@ public class Main extends Applet {
                 if (args[startargs].startsWith("http://")) {
                     if (Log.on) Log.log(Main.class, "downloading xwar");
                     URL u = new URL(args[startargs]);
-                    originAddr = InetAddress.getByName(u.getHost());
-                    Resources.loadArchive(Platform.urlToInputStream(u));
+                    try {
+                        originAddr = InetAddress.getByName(u.getHost());
+                    } catch (UnknownHostException) {
+                        if (Log.on) Log.log("couldn't resolve " + u.getHost() + " -- hopefully there is a proxy that can");
+                    }
+                    Resources.loadArchive(new HTTP(args[startargs]).getInputStream());
                     
                 } else {
 
index a4b67db..39a480f 100644 (file)
@@ -21,14 +21,20 @@ public class Platform {
 
     // Static Data /////////////////////////////////////////////////////////////////////////////////////
 
-    /** set to true during the delivery of a KeyPressed:C-v/A-v or Press3; it is safe to use a 'global' here,
-     *  since message delivery is single-threaded and non-preemptable
+    /**
+     *  set to true during the delivery of a KeyPressed:C-v/A-v or Press3; it is safe to use a
+     *  'global' here, since message delivery is single-threaded and non-preemptable
      */
     static boolean clipboardReadEnabled = false;
 
     /** The appropriate Platform object for this JVM */
     static Platform platform = null;
 
+    /** true if proxy autodetection has already been run */
+    static boolean alreadyDetectedProxy = false;
+
+    /** the result of proxy autodetection */
+    static HTTP.ProxyInfo cachedProxyInfo = null;
 
     // VM Detection Logic /////////////////////////////////////////////////////////////////////
 
@@ -47,8 +53,8 @@ public class Platform {
             String os_name = System.getProperty("os.name", "");
             String platform_class = null;
             
-            if (os_name.startsWith("Mac OS X")) platform_class = "MacOSX";
-            else if (vendor.startsWith("Free Software Foundation")) platform_class = "Win32";
+            //if (os_name.startsWith("Mac OS X")) platform_class = "MacOSX";
+            if (vendor.startsWith("Free Software Foundation")) platform_class = "Win32";
             else if (version.startsWith("1.1") && vendor.startsWith("Netscape")) platform_class = "Netscape";
             else if (version.startsWith("1.1") && vendor.startsWith("Microsoft")) platform_class = "Microsoft";
             else if (version.startsWith("1.4")) platform_class = "Java14";
@@ -58,6 +64,7 @@ public class Platform {
                 platform = (Platform)Class.forName("org.xwt.plat." + platform_class).newInstance();
                 platform.init();
             }
+
             if (Log.on) Log.log(Platform.class, "XWT VM detection:   vendor = " + vendor);
             if (Log.on) Log.log(Platform.class, "                   version = " + version);
             if (Log.on) Log.log(Platform.class, "                        os = " + os_name);
@@ -162,6 +169,12 @@ public class Platform {
         return;
     }
 
+    /** Returns true iff the platform detected that it should be using a proxy.
+     *  The platform implementation should cache the result.
+     *  This method will only be called if a connection attempt failed
+     */
+    protected synchronized HTTP.ProxyInfo _detectProxy() { return null; }
+
     // Static methods -- thunk to the instance /////////////////////////////////////////////////////////////////////////
 
     /** if true, org.xwt.Surface should generate Click messages automatically when a Release happens after a Press and the mouse has not moved much */
@@ -218,7 +231,7 @@ public class Platform {
     /** creates and returns a picture */
     public static Picture createPicture(ImageDecoder i) { return platform._createPicture(i.getData(), i.getWidth(), i.getHeight()); }
 
-    /** creates and returns a picture */
+    /** opens a new browser window */
     public static void newBrowserWindow(String url) {
         if (!(url.startsWith("https://") || url.startsWith("http://") || url.startsWith("ftp://") || url.startsWith("mailto:"))) {
             if (Log.on) Log.log(Platform.class, "xwt.newBrowserWindow() only supports http and https urls");
@@ -270,6 +283,28 @@ public class Platform {
         return ret;
     }
 
+    /** detects proxy settings */
+    public static synchronized HTTP.ProxyInfo detectProxy() {
+
+        if (cachedProxyInfo != null) return cachedProxyInfo;
+        if (alreadyDetectedProxy) return null;
+        alreadyDetectedProxy = true;
+
+        if (Log.on) Log.log(Platform.class, "attempting xwt-proxy DNS proxy detection");
+        cachedProxyInfo = HTTP.ProxyInfo.detectProxyViaManual();
+        if (cachedProxyInfo != null) return cachedProxyInfo;
+
+        if (Log.on) Log.log(Platform.class, "attempting " + platform.getClass().getName() + " proxy detection");
+        cachedProxyInfo = platform._detectProxy();
+        if (cachedProxyInfo != null) return cachedProxyInfo;
+
+        if (Log.on) Log.log(Platform.class, "attempting WPAD proxy detection");
+        cachedProxyInfo = HTTP.ProxyInfo.detectProxyViaWPAD();
+        if (cachedProxyInfo != null) return cachedProxyInfo;
+
+        return cachedProxyInfo;
+    }
+
     // Helpful font parsing stuff //////////////////////////////////////////////////////
 
     public static class ParsedFont {
@@ -322,3 +357,4 @@ public class Platform {
 
 }
 
+
index d993fe8..35ecd50 100644 (file)
@@ -212,8 +212,7 @@ class SOAP extends XMLRPC {
         }
     }
 
-    protected Object send(Object[] args, BufferedReader br, PrintWriter ps) throws IOException, JavaScriptException {
-
+    protected String send(Object[] args, HTTP http) throws JavaScriptException {
         // build up the request
         StringBuffer content = new StringBuffer();
         content.append("<?xml version=\"1.0\"?>\n");
@@ -233,37 +232,12 @@ class SOAP extends XMLRPC {
                 appendObject(o[i].toString(), ((Scriptable)args[0]).get(o[i].toString(), (Scriptable)args[0]), content);
         }
         content.append("    </" + methodname + "></SOAP-ENV:Body></SOAP-ENV:Envelope>");
-        
-        // send it
-        ps.print("POST " + filename + " HTTP/1.0\r\n");
-        ps.print("Host: " + host + "\r\n");
-        ps.print("User-Agent: XWT (http://www.xwt.org/)\r\n");
-        ps.print("Content-Type: text/xml; charset=\"us-ascii\"\r\n");
-        ps.print("Content-length: " + (content.length() + 1) + "\r\n");
-        ps.print("SOAPAction: " + (action == null ? filename : action) + "\r\n");
-        ps.print("\r\n");
-        ps.print(content);
-        ps.print("\n");
-        ps.flush();
-        
-        // throw away HTTP reply headers
-        while(!br.readLine().equals("")) { }
-
-        // parse XML reply
-        try {
-            parse(br);
-        } catch (XML.SAXException e) {
-            if (Log.on) Log.log(this, "reply from server was not well-formed XML: " + e);
-            throw new JavaScriptException("reply from server was not well-formed XML: " + e);
-        }
-
-        if (fault) throw new JavaScriptException((Scriptable)objects.elementAt(0));
-        if (objects.size() == 0) return null;
-        return objects.elementAt(0);
+        http.addHeader("SOAPAction", action);
+        return content.toString();
     }
 
-    SOAP(String urlstr, String methodname, String action, String nameSpace) {
-        super(urlstr, methodname);
+    SOAP(String url, String methodname, String action, String nameSpace) {
+        super(url, methodname);
         this.action = action;
         this.nameSpace = nameSpace;
     }
index 4da6b1c..3201ecd 100644 (file)
@@ -32,24 +32,12 @@ import org.bouncycastle.util.encoders.Base64;
  */
 class XMLRPC extends XML implements Function {
 
-    /** should we use SSL? */
-    protected boolean ssl = false;
-
     /** the url to connect to */
-    protected URL url = null;
+    protected String url = null;
 
     /** the method name to invoke on the remove server */
     protected String methodname = null;
 
-    /** the host portion of the url; not calculated until first call() */
-    protected String host = null;
-
-    /** the filename portion of the url; not calculated until first call() */
-    protected String filename = null;
-
-    /** the port to connect to; not calculated until the first call() */
-    protected int port = -1;
-
     /** this holds character content as we read it in -- since there is only one per instance, we don't support mixed content */
     protected AccessibleCharArrayWriter content = new AccessibleCharArrayWriter(100);
 
@@ -169,6 +157,7 @@ class XMLRPC extends XML implements Function {
         }
     }
 
+
     // Methods to make outbound XML-RPC request ///////////////////////////////////////////////////
 
     /** Appends the XML-RPC representation of <code>o</code> to <code>sb</code> */
@@ -242,74 +231,50 @@ class XMLRPC extends XML implements Function {
     }
 
     private Object connect(Object[] args) throws JavaScriptException, IOException {
-        if (filename == null) {
-            filename = url.getFile();
-            host = url.getHost();
-            port = url.getPort();
-
-            InetAddress addr;
-            try { addr = InetAddress.getByName(host); }
-            catch (UnknownHostException uhe) { throw new JavaScriptException("could not resolve hostname \"" + host + "\""); }
-            byte[] quadbyte = addr.getAddress();
-            
-            if (quadbyte[0] == 10 ||
-                (quadbyte[0] == 192 && quadbyte[1] == 168) ||
-                (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16) &&
-                !addr.equals(Main.originAddr)) {
-                filename = null;
-                throw new JavaScriptException("security violation: " + host + " [" + addr.getHostAddress() + "] is in a firewalled netblock");
-            }
-        }
-
-        Socket s;
-        if (ssl) s = Platform.getSocket(host, port == -1 ? 443 : port, true);
-        else if (url.getProtocol().equals("http")) s = Platform.getSocket(host, port == -1 ? 80 : port, false);
-        else throw new JavaScriptException("only http[s] is supported");
-
-        s.setTcpNoDelay(true);
-        OutputStream os = new BufferedOutputStream(s.getOutputStream(), 4000);
-        InputStream is = new BufferedInputStream(new Filter(s.getInputStream()));
-        
-        PrintWriter ps;
-        if (!Log.verbose) ps = new PrintWriter(os);
-        else ps = new PrintWriter(new FilterWriter(new OutputStreamWriter(os)) {
-                public void write(int i) throws IOException {
-                    super.write(i);
-                    if (Log.on) Log.log(this, "send: " + ((char)i));
-                }
-                public void write(String s, int start, int len) throws IOException {
-                    super.write(s, start, len);
-                    if (Log.on) Log.log(this, "send: " + s.substring(start, start + len));
-                }
-                public void write(char[] c, int start, int len) throws IOException {
-                    super.write(c, start, len);
-                    if (Log.on) Log.log(this, "send: " + new String(c, start, len));
-                }
-            });
+        if (Log.verbose) Log.log(this, "call to " + url + " : " + methodname);
+        HTTP http = new HTTP(url);
+        String content = send(args, http);
+        OutputStream os = new BufferedOutputStream(http.getOutputStream(content.length(), "text/xml"), 4000);
+        PrintWriter ps = !Log.verbose ?
+            new PrintWriter(os) :
+            new PrintWriter(new FilterWriter(os)) {
+                    public void write(int i) throws IOException {
+                        super.write(i);
+                        if (Log.on) Log.log(this, "send: " + ((char)i));
+                    }
+                    public void write(String s, int start, int len) throws IOException {
+                        super.write(s, start, len);
+                        if (Log.on) Log.log(this, "send: " + s.substring(start, start + len));
+                    }
+                    public void write(char[] c, int start, int len) throws IOException {
+                        super.write(c, start, len);
+                        if (Log.on) Log.log(this, "send: " + new String(c, start, len));
+                    }
+                });
+        ps.print(content.toString());
+        ps.flush();
         
-        BufferedReader br;
-        if (!Log.verbose) br = new BufferedReader(new InputStreamReader(is));
-        else br = new BufferedReader(new FilterReader(new InputStreamReader(is)) {
+        BufferedReader br = !Log.verbose ?
+            new BufferedReader(new InputStreamReader(new Filter(http.getInputStream()))) :
+            new BufferedReader(new FilterReader(new InputStreamReader(new Filter(http.getInputStream()))) {
                 public int read() throws IOException {
                     int i = super.read();
+                    System.out.println("X " + i);
                     if (Log.on) Log.log(this, "recv: " + ((char)i));
                     return i;
                 }
                 public int read(char[] c, int off, int len) throws IOException {
                     int ret = super.read(c, off, len);
+                    System.out.println("Y " + ret);
                     if (ret == -1) return ret;
                     if (Log.on) Log.log(this, "recv: " + new String(c, off, ret));
                     return ret;
                 }
             });
-        
-        if (Log.verbose) Log.log(this, "call to " + url + " : " + methodname);
-        return send(args, br, ps);
+        return recieve(br);
     }
 
-    protected Object send(Object[] args, BufferedReader br, PrintWriter ps) throws JavaScriptException, IOException {
-
-        // Construct payload
+    protected String send(Object[] args, HTTP http) throws JavaScriptException {
         StringBuffer content = new StringBuffer();
         content.append("<?xml version=\"1.0\"?>\n");
         content.append("    <methodCall>\n");
@@ -326,21 +291,10 @@ class XMLRPC extends XML implements Function {
             content.append("        </params>\n");
         }
         content.append("    </methodCall>");
+        return content.toString();
+    }
         
-        // Header
-        ps.print("POST " + filename + " HTTP/1.0\r\n");
-        ps.print("Host: " + host + "\r\n");
-        ps.print("User-Agent: XWT (http://www.xwt.org/)\r\n");
-        ps.print("Content-Type: text/xml\r\n");
-        ps.print("Content-length: " + (content.length() + 1) + "\r\n");
-        ps.print("\r\n");
-        ps.print(content);
-        ps.print("\n");
-        ps.flush();
-
-        // throw away HTTP reply headers
-        while(!br.readLine().equals("")) { }
-
+    protected Object recieve(BufferedReader br) throws JavaScriptException, IOException {
         // parse XML reply
         try {
             parse(br);
@@ -350,10 +304,8 @@ class XMLRPC extends XML implements Function {
         }
         
         if (fault) throw new JavaScriptException(objects.elementAt(0));
-        
         if (objects.size() == 0) return null;
         return objects.elementAt(0);
-
     }
 
     public final Object call(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) throws JavaScriptException {
@@ -401,28 +353,15 @@ class XMLRPC extends XML implements Function {
 
     /** When you get a property from an XMLRPC, it just returns another XMLRPC with the property name tacked onto methodname. */
     public Object get(String name, Scriptable start) {
-        return new XMLRPC(url.toString(), (methodname.equals("") ? "" : methodname + ".") + name, ssl);
+        return new XMLRPC(url, (methodname.equals("") ? "" : methodname + ".") + name);
     }
 
-    public XMLRPC(String urlstr, String methodname) { this(urlstr, methodname, false); }
-    public XMLRPC(String urlstr, String methodname, boolean ssl) {
-        this.ssl = ssl;
-        try {
-            if (urlstr.startsWith("https:")) {
-                urlstr = "http" + urlstr.substring(5);
-                this.ssl = true;
-            }
-            URL url = new URL(urlstr);
-            if (methodname == null) methodname = "";
-            this.methodname = methodname;
-            this.url = url;
-
-        } catch (MalformedURLException e) {
-            if (Log.on) Log.log(this, e);
-
-        }
+    public XMLRPC(String url, String methodname) {
+        this.url = url;
+        this.methodname = methodname;
     }
 
+
     // Helper Classes ///////////////////////////////////////////////////////////////////////////////////
 
     /** CharArrayWriter that lets us touch its buffer */
@@ -451,6 +390,7 @@ class XMLRPC extends XML implements Function {
         }
     }
 
+
     // Methods Required by Rhino ////////////////////////////////////////////////////////
 
     public String getClassName() { return "XMLRPC"; }
index a159eae..0036405 100644 (file)
@@ -10,12 +10,54 @@ import java.io.*;
 import java.util.*;
 import org.xwt.util.*;
 import org.xwt.*;
+import java.lang.reflect.*;
 
-// FEATURE: Java 1.4 allows undecorated frames and can maximize windows
 
 /** Platform class for most reasonable Java1.2+ JVMs */
 public class Java12 extends AWT {
 
+    /** this is done with reflection in case a new version of the plugin comes out that doesn't let us pull the sun.plugin.* trick */
+    protected synchronized HTTP.ProxyInfo _detectProxy() {
+        return (HTTP.ProxyInfo)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
+                public Object run() {
+                    try {
+                        HTTP.ProxyInfo pi = new HTTP.ProxyInfo();
+                        
+                        Class PluginProxyHandler = Class.forName("sun.plugin.protocol.PluginProxyHandler");
+                        Method getDefaultProxyHandler = PluginProxyHandler.getMethod("getDefaultProxyHandler", new Class[] { });
+                        Object proxyHandler = getDefaultProxyHandler.invoke(null, new Object[] { });
+                        
+                        Class ProxyHandler = Class.forName("sun.plugin.protocol.ProxyHandler");
+                        Method getProxyInfo = ProxyHandler.getMethod("getProxyInfo", new Class[] { URL.class });
+                        Object proxyInfo = getProxyInfo.invoke(proxyHandler, new Object[] { new URL("http://www.xwt.org") });
+                        
+                        Class ProxyInfo = Class.forName("sun.plugin.protocol.ProxyInfo");
+                        
+                        if (((Boolean)ProxyInfo.getMethod("isSocksUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue()) {
+                            pi.socksProxyHost =
+                                (String)ProxyInfo.getMethod("getSocksProxy", new Class[] { }).invoke(proxyInfo, new Object[] { });
+                            pi.socksProxyPort =
+                                ((Integer)ProxyInfo.getMethod("getSocksPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
+                        }
+                        
+                        if (((Boolean)ProxyInfo.getMethod("isProxyUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue()) {
+                            pi.httpProxyHost =
+                                (String)ProxyInfo.getMethod("getProxy", new Class[] { }).invoke(proxyInfo, new Object[] { });
+                            pi.httpProxyPort =
+                                ((Integer)ProxyInfo.getMethod("getPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
+                        }
+                        
+                        if (pi.httpProxyHost != null || pi.socksProxyHost != null) return pi;
+                        else return null;
+
+                    } catch (Throwable e) {
+                        if (Log.on) Log.log(this, "exception while querying sun.plugin.protocol.PluginProxyHandler:");
+                        if (Log.on) Log.log(this, e);
+                        return null;
+                    }
+                }});
+    }
+
     protected Socket __getSocket(String host, int port, boolean ssl) throws IOException { return super._getSocket(host, port, ssl); }
     protected Socket _getSocket(final String host, final int port, final boolean ssl) throws IOException {
         return (Socket)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
index cb4eafe..f36c239 100644 (file)
@@ -1,6 +1,6 @@
 <!-- Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL] -->
 
-<project name="Java2" default="run" basedir=".">
+<project name="Java12" default="run" basedir=".">
 
     <target name="build"/>
 
@@ -17,9 +17,9 @@
     </target>
 
     <target name="dist">
-        <echo message="creating jar source area in bin-Java2/"/>
-        <mkdir dir="bin-Java2"/>
-        <copy todir="bin-Java2">
+        <echo message="creating jar source area in bin-Java12/"/>
+        <mkdir dir="bin-Java12"/>
+        <copy todir="bin-Java12">
             <fileset dir="bin/">
                 <include name='org/xwt/*.class'/>
                 <include name='org/xwt/util/*.class'/>
index acc51bd..3bb342e 100644 (file)
@@ -13,6 +13,9 @@
 #include <windows.h>
 #include <mmsystem.h>
 #undef STRICT
+#undef MAX_PRIORITY
+#undef MIN_PRIORITY
+#undef NORM_PRIORITY
 
 #include <gcj/cni.h>
 
@@ -161,6 +164,7 @@ static unsigned char hand_cursor_and[32 * 4] = {
   0xFF, 0xFF, 0xFF, 0xFF
 };
 
+
 void org::xwt::plat::Win32::natInit() {
 
     // grab desktop dc/handle
@@ -233,6 +237,41 @@ void org::xwt::plat::Win32::natInit() {
 
 // Platform Methods ///////////////////////////////////////////////////////////////////
 
+void org::xwt::plat::Win32::__detectProxy(JArray<jstring>* container) {
+
+    HKEY hkey;
+    char buf[1024];
+    DWORD buflen = 1024;
+    DWORD type;
+    LONG result = RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
+    if (result != ERROR_SUCCESS) return;
+    
+    buf[0] = '\0';
+    type = REG_SZ;
+    buflen = 1024;
+    result = RegQueryValueEx(hkey, "AutoConfigURL", NULL, &type, (LPBYTE)buf, &buflen);
+    buf[buflen] = '\0';
+    if (result == ERROR_SUCCESS) elements(container)[2] = JvNewStringLatin1(buf);
+
+    buf[0] = '\0';
+    type = REG_BINARY;
+    RegQueryValueEx(hkey, "ProxyEnable", NULL, &type, (LPBYTE)buf, &buflen);
+    if (buf[0] != 1) return;
+
+    buf[0] = '\0';
+    type = REG_SZ;
+    buflen = 1024;
+    RegQueryValueEx(hkey, "ProxyServer", NULL, &type, (LPBYTE)buf, &buflen);
+    buf[buflen] = '\0';
+    elements(container)[0] = JvNewStringLatin1(buf);
+
+    buf[0] = '\0';
+    buflen = 1024;
+    RegQueryValueEx(hkey, "ProxyOverride", NULL, &type, (LPBYTE)buf, &buflen);
+    buf[buflen] = '\0';
+    elements(container)[1] = JvNewStringLatin1(buf);
+}
+
 jstring org::xwt::plat::Win32::_getClipBoard() {
     OpenClipboard((HWND)desktop_handle);
     HGLOBAL hmem = GetClipboardData(CF_TEXT);
@@ -618,6 +657,7 @@ jint org::xwt::plat::Win32$Win32Surface::WndProc(jint _hwnd, jint _iMsg, jint _w
     UINT iMsg = (UINT)_iMsg;
     WPARAM wParam = (WPARAM)_wParam;
     LPARAM lParam = (LPARAM)_lParam;
+
     int oldmousex, oldmousey;
     MINMAXINFO* mmi;
     POINT point;
index 65f79ec..f9172d0 100644 (file)
@@ -117,6 +117,57 @@ public class Win32 extends GCJ {
     protected native String _getClipBoard();
     protected native void _setClipBoard(String s);
 
+    private native void __detectProxy(String[] container);
+
+    protected synchronized HTTP.ProxyInfo _detectProxy() {
+
+        String[] container = new String[] { null, null, null };
+        if (Log.on) Log.log(this, "accessing Win32 registry");
+        __detectProxy(container);
+        if (container[2] == null && container[0] == null) {
+            if (Log.on) Log.log(this, "no proxy settings in the Win32 registry");
+            return null;
+        }
+        
+        if (Log.on) Log.log(this, "PAC Script URL: " + container[2]);
+        if (Log.on) Log.log(this, "Proxy Server String: " + container[0]);
+        if (Log.on) Log.log(this, "Proxy Override String: " + container[1]);
+
+        HTTP.ProxyInfo ret = new HTTP.ProxyInfo();
+        if (container[2] != null) {
+            ret.proxyAutoConfigFunction = HTTP.ProxyInfo.getProxyAutoConfigFunction(container[2]);
+            if (ret.proxyAutoConfigFunction != null) return ret;
+        }
+
+        if (container[0] = null) return null;
+        StringTokenizer st = new StringTokenizer(container[0], ";", false);
+        while(st.hasMoreTokens()) try {
+            String s = st.nextToken().trim();
+            if (s.indexOf('=') == -1) continue;
+            if (s.indexOf(':') == -1) continue;
+            String protocol = s.substring(0, s.indexOf('='));
+            String host = s.substring(s.indexOf('=') + 1, s.indexOf(':'));
+            int port = Integer.parseInt(s.substring(s.indexOf(':') + 1));
+            if (protocol.equals("http")) {
+                ret.httpProxyHost = host;
+                ret.httpProxyPort = port;
+            } else if (protocol.equals("https")) {
+                ret.httpsProxyHost = host;
+                ret.httpsProxyPort = port;
+            } else if (protocol.equals("socks")) {
+                ret.socksProxyHost = host;
+                ret.socksProxyPort = port;
+            }
+        } catch (NumberFormatException nfe) { }
+
+        if (container[1] != null) {
+            st = new StringTokenizer(container[1], ";", false);
+            ret.excluded = new String[st.countTokens()];
+            for(int i=0; st.hasMoreTokens(); i++) ret.excluded[i] = st.nextToken();
+        }
+        return ret;
+    }
+
     protected native boolean _newBrowserWindow_(String url);
     protected void _newBrowserWindow(String url) {
         if (!_newBrowserWindow_(url))
@@ -150,11 +201,7 @@ public class Win32 extends GCJ {
         public native void natInit(boolean framed);
         public Win32Surface(Box b, final boolean framed) {
             super(b);
-            if (Log.on) Log.log(this, "before, hwnd = " + hwnd);
-            if (Log.on) Log.log(this, "before, hdc = " + hdc);
             natInit(framed);
-            if (Log.on) Log.log(this, "after, hwnd = " + hwnd);
-            if (Log.on) Log.log(this, "after, hdc = " + hdc);
             hwndToWin32SurfaceMap.put(new Integer(hwnd), this);
         }