Java authentification (Was Getting the http username/password for a part)

Koos Vriezen koos.vriezen at xs4all.nl
Fri May 2 23:21:09 BST 2003


On Fri, 7 Mar 2003, Koos Vriezen wrote:

> Hi,
>
> My first attempt for loading applets for sites using HTTP based
> authentification.
> Java uses an Authenticator class that is host based (without a path).
>
> Auth is checked on host, port and authname.
>
> Unfortunaly I can't get a handle to the classloader/context triggering
> this request, so authname/realm is the only protection again malicious
> homepages on sites containing secure websites.
>
> Todo: add multible entries per host

I give up this idea. For cookie based authentication the only way seems to
implement a HttpURLConnection and create it in a URLStreamHandlerFactory
set by java.net.URL.setURLStreamHandlerFactory.
For this, I revamped the KJavaDownloader. It was used for the
KJASClassLoader a long time ago. I changed it for use of HttpURLConnection.
It already seems to work nicely, though lot is not implemented yet (like
POST's). Benefit are caching over jvm sessions and finally got webmin
applets to work.

Comments?

Koos
-------------- next part --------------
package org.kde.kjas.server;

import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
/**
 * ClassLoader used to download and instantiate Applets.
 */

final class KJASHttpURLConnection extends HttpURLConnection
{
    final static int DATA = 0;
    final static int FINISHED = 1;
    final static int ERRORCODE = 2;
    final static int HEADERS = 3;
    final static int REDIRECT = 4;
    final static int MIMETYPE = 5;

    private static int id = 0;
    public static Hashtable jobs = new Hashtable();

    public Thread thread = null;

    private boolean connected = false;
    private String jobid = null;
    private LinkedList data = new LinkedList ();
    private Vector headers = new Vector();
    private Hashtable headersmap = new Hashtable();
    private int errorcode = 0;
    private boolean finished = false;

    private void checkConnected() throws IOException {
        if (!connected)
            throw new IOException("not connected");
    }

    final class KJASOutputStream extends OutputStream {
        KJASOutputStream() {
        }
        public void write(int b) throws IOException {
            /*checkConnected();
            int res = 0;
            synchronized (sema) {
                res = kioWrite (id, b);
            }
            if (res != 0) {
                disconnect();
                throw new IOException("i/o error");
            }*/
            throw new IOException("not implemented");
        }
        public void close() throws IOException {
            checkConnected();
            disconnect();
        }
        public void flush() throws IOException {
            checkConnected();
            /*int res = 0;
            synchronized (sema) {
                res = kioFlush (id);
            }
            if (res != 0) {
                disconnect();
                throw new IOException("i/o error");
            }*/
            throw new IOException("not implemented");
        }
    }

    final class KJASInputStream extends InputStream {
        private byte [] buf = null;
        private int bufpos = 0;
        private boolean eof = false;

        KJASInputStream() {
        }
        private boolean getData() throws IOException {
            if (eof)
                return false;
            checkConnected();
            if (buf != null && bufpos < buf.length)
                return true;
            synchronized (jobs) {
                if (data.size() > 0) {
                    buf = (byte []) data.removeFirst();
                    bufpos = 0;
                    return true;
                }
                if (finished) {
                    eof = true;
                    disconnect();
                    return false;
                }
                thread = Thread.currentThread();
            }
            try {
                Thread.currentThread().sleep(10000);
            } catch (InterruptedException ie) {
                thread = null;
                return getData();
            }
            thread = null;
            disconnect();
            throw new IOException("timeout");
        }
        public int read() throws IOException {
            if (getData())
                return 0x00ff & buf[bufpos++];
            return -1;
        }
        public int read(byte[] b, int off, int len) throws IOException {
            if (!getData())
                return -1;
            int nr = buf.length - bufpos;
            if (nr > len)
                nr = len;
            System.arraycopy(buf, bufpos, b, off, nr);
            bufpos += nr;
            return nr;
        }
        public int read(byte[] b) throws IOException {
            return read(b, 0, b.length);
        }
        public int available() throws IOException {
            if (eof)
                return 0;
            checkConnected();
            return buf == null ? 0 : buf.length - bufpos;
        }
        public boolean markSupported() {
            return false;
        }
        public void close() throws IOException {
            checkConnected();
            disconnect();
        }
    }

    private KJASOutputStream out = null;
    private KJASInputStream in = null;

    KJASHttpURLConnection(URL u) {
        super(u);
    }
    public void setData(int code, byte [] d) {
        // this method is synchronized on jobs in processCommand
        switch (code) {
            case FINISHED:
                finished = true;
                break;
            case DATA:
                if (d.length > 0)
                    data.addLast(d);
                Main.debug ("XXXXXXX DATA " + data.size());
                break;
            case ERRORCODE:
                Main.debug ("XXXXXXX RESPONSECODE " + d.length);
                if (d.length >= 4)
                for (int i =0; i < 4; i++)
                    errorcode |= ((d[i] & 0x00ff) << ((4 - i) * 8));
                Main.debug ("ERRORECODE " + errorcode);
                break;
            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())
                    };
                    headers.add(entry);
                    headersmap.put(entry[0], entry[1]);
                    Main.debug ("XXXXXXX header " + entry[0] + "=" + entry[1]);
                }
        }
    }
    public Map getHeaderFields() {
        Main.debug ("XXXXXXX getHeaderFields");
        return headersmap;
    }
    public String getHeaderField(String name) {
        Main.debug ("XXXXXXX getHeaderField " + name);
        return (String) headersmap.get(name);
    }
    public String getHeaderField(int n) {
        Main.debug ("XXXXXXX getHeaderField " + n);
        if (n >= headersmap.size())
            return null;
        String [] entry = (String []) headers.get(n);
        String line = entry[0];
        if (entry[1].length() > 0)
            line += ":" + entry[1];
        return line;
    }
    public String getHeaderFieldKey(int n) {
        Main.debug ("XXXXXXX getHeaderFieldKey " + n);
        if (n >= headersmap.size())
            return null;
        return ((String []) headers.get(n))[0];
    }
    public boolean usingProxy() {
        return false; // FIXME
    }
    public synchronized void connect() throws IOException {
        Main.debug ("XXXXXX connect " + url + " redir=" + getFollowRedirects());
        if (connected)
            throw new IOException("already connected");
        synchronized (jobs) {
            jobid = String.valueOf(id++);
            thread = Thread.currentThread();
            jobs.put(jobid, this);
        }
        if (doInput)
            Main.protocol.sendGetURLDataCmd(jobid, url.toExternalForm());
        try {
            Thread.currentThread().sleep(20000);
        } catch (InterruptedException ie) {
            connected = true;
            if (doInput)
                in = new KJASInputStream();
            else
                out = new KJASOutputStream();
            Main.debug ("XXXXXXX connect content-length=" + getContentLength());
            if (getResponseCode() < 400) {
                thread = null;
                return;
            }
        }
        synchronized (jobs) {
            jobs.remove(jobid);
        }
        thread = null;
        if (connected) {
            Main.debug ("XXXXXXX connect error");
            throw new IOException("connection failed (not found)");
        }
        Main.debug ("XXXXXXX connect timeout");
        throw new IOException("connection failed (timeout)");
    }
    public void disconnect() {
        if (!connected)
            return;
        synchronized (jobs) {
            jobs.remove(jobid);
        }
        connected = false;
        out = null;
        in = null;
    }
    public InputStream getInputStream() throws IOException {
        Main.debug ("XXXXXXX getInputStream " + url);
        //(new Exception()).printStackTrace();
        if (!connected)
            connect();
        return in;
    }
    public OutputStream getOutputStream() throws IOException {
        Main.debug ("XXXXXXX getOutputStream " + url);
        if (!connected)
            connect();
        return out;
    }
}

final class KJASHttpURLStreamHandler extends URLStreamHandler
{
    KJASHttpURLStreamHandler() {
    }
    protected URLConnection openConnection(URL u) throws IOException {
        Main.debug ("XXXXXXX openConnection " + u);
        return new KJASHttpURLConnection(u);
    }
    protected int getDefaultPort() {
        return 80;
    }
}

public final class KJASURLStreamHandlerFactory 
    implements URLStreamHandlerFactory
{
    public URLStreamHandler createURLStreamHandler(String protocol) {
        Main.debug ("XXXXXXX createURLStreamHandler " + protocol);
        if (protocol.equals("http"))
            return new KJASHttpURLStreamHandler();
        return null;
    }
}
-------------- next part --------------
Index: kjavaappletserver.cpp
===================================================================
RCS file: /home/kde/kdelibs/khtml/java/kjavaappletserver.cpp,v
retrieving revision 1.59
diff -u -3 -p -r1.59 kjavaappletserver.cpp
--- kjavaappletserver.cpp	7 Mar 2003 12:12:48 -0000	1.59
+++ kjavaappletserver.cpp	2 May 2003 22:02:24 -0000
@@ -387,13 +387,11 @@ void KJavaAppletServer::stopApplet( int 
     process->send( KJAS_STOP_APPLET, args );
 }
 
-void KJavaAppletServer::sendURLData( const QString& loaderID,
-                                     const QString& url,
-                                     const QByteArray& data )
+void KJavaAppletServer::sendURLData( int loaderID, int code, const QByteArray& data )
 {
     QStringList args;
-    args.append( loaderID );
-    args.append( url );
+    args.append( QString::number(loaderID) );
+    args.append( QString::number(code) );
 
     process->send( KJAS_URLDATA, args, data );
 
@@ -509,21 +507,21 @@ void KJavaAppletServer::slotJavaRequest(
             break;
     }
 
+    bool ok;
+    int contextID_num = contextID.toInt( &ok );
+
+    if( !ok )
+    {
+        kdError(6100) << "could not parse out contextID to call command on" << endl;
+        return;
+    }
+
     if( cmd_code == KJAS_GET_URLDATA )
     {
-        new KJavaDownloader( contextID, args[0] );
+        new KJavaDownloader( contextID_num, args[0] );
     }
     else
     {
-        bool ok;
-        int contextID_num = contextID.toInt( &ok );
-
-        if( !ok )
-        {
-            kdError(6100) << "could not parse out contextID to call command on" << endl;
-            return;
-        }
-
         KJavaAppletContext* context = d->contexts[ contextID_num ];
         if( context )
             context->processCmd( cmd, args );
Index: kjavaappletserver.h
===================================================================
RCS file: /home/kde/kdelibs/khtml/java/kjavaappletserver.h,v
retrieving revision 1.24
diff -u -3 -p -r1.24 kjavaappletserver.h
--- kjavaappletserver.h	7 Mar 2003 12:12:48 -0000	1.24
+++ kjavaappletserver.h	2 May 2003 22:02:24 -0000
@@ -118,8 +118,7 @@ public:
      * class loader.
      * (This is currently unimplemented on the java side.
      */
-    void sendURLData( const QString& loaderID, const QString& url,
-                      const QByteArray& data );
+    void sendURLData( int loaderID, int code, const QByteArray& data );
 
     /**
      * Shut down the KJAS server.
Index: kjavadownloader.cpp
===================================================================
RCS file: /home/kde/kdelibs/khtml/java/kjavadownloader.cpp,v
retrieving revision 1.5
diff -u -3 -p -r1.5 kjavadownloader.cpp
--- kjavadownloader.cpp	27 Feb 2003 22:42:52 -0000	1.5
+++ kjavadownloader.cpp	2 May 2003 22:02:24 -0000
@@ -27,25 +27,35 @@
 #include <kdebug.h>
 #include <qfile.h>
 
+static const int DATA = 0;
+static const int FINISHED = 1;
+static const int RESPONSECODE = 2;
+static const int HEADERS = 3;
+static const int REDIRECT = 4;
+static const int MIMETYPE = 5;
+                
 class KJavaDownloaderPrivate
 {
 friend class KJavaDownloader;
 public:
+    KJavaDownloaderPrivate() : responseCode(0), isfirstdata(true) {}
     ~KJavaDownloaderPrivate()
     {
         if( url )
         delete url;
     }
 private:
-    QString           loaderID;
+    int               loaderID;
     KURL*             url;
     QByteArray        file;
     KIO::TransferJob* job;
+    int               responseCode;
+    bool              isfirstdata;
 };
 
 
 /* KDE 4: Make them const QString & */
-KJavaDownloader::KJavaDownloader( QString& ID, QString& url )
+KJavaDownloader::KJavaDownloader( int ID, const QString& url )
 {
     kdDebug(6100) << "KJavaDownloader for ID = " << ID << " and url = " << url << endl;
 
@@ -55,8 +65,13 @@ KJavaDownloader::KJavaDownloader( QStrin
     d->url = new KURL( url );
 
     d->job = KIO::get( url, false, false );
+    d->job->addMetaData("PropagateHttpHeader", "true");
     connect( d->job,  SIGNAL(data( KIO::Job*, const QByteArray& )),
              this,    SLOT(slotData( KIO::Job*, const QByteArray& )) );
+    connect( d->job, SIGNAL(connected(KIO::Job*)),
+             this, SLOT(slotConnected(KIO::Job*)));
+    connect( d->job, SIGNAL(mimetype(KIO::Job*, const QString&)),
+             this, SLOT(slotMimetype(KIO::Job*, const QString&)));
     connect( d->job, SIGNAL(result(KIO::Job*)),
              this,   SLOT(slotResult(KIO::Job*)) );
 }
@@ -70,24 +85,60 @@ void KJavaDownloader::slotData( KIO::Job
 {
     kdDebug(6100) << "slotData for url = " << d->url->url() << endl;
 
-    int cur_size = d->file.size();
+    KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
+    if (d->isfirstdata) {
+        QString headers = d->job->queryMetaData("HTTP-Headers");
+        if (!headers.isEmpty()) {
+            d->file.resize( headers.length() );
+            memcpy( d->file.data(), headers.ascii(), headers.length() );
+            server->sendURLData( d->loaderID, HEADERS, d->file );
+        }
+        d->isfirstdata = false;
+        KIO::MetaData md = d->job->metaData();
+        KIO::MetaData::ConstIterator it;
+        for( it = md.begin(); it !=  md.end(); ++it)
+            kdDebug(6100)<< "metadata " << it.key() << "=" << it.data() << endl;
+    }
     int qb_size = qb.size();
-    d->file.resize( cur_size + qb_size );
-    memcpy( d->file.data() + cur_size, qb.data(), qb_size );
+    if (qb_size) {
+        d->file.resize( qb_size );
+        memcpy( d->file.data(), qb.data(), qb_size );
+        server->sendURLData( d->loaderID, DATA, d->file );
+        d->file.resize( 0 );
+    }
+    KJavaAppletServer::freeJavaServer();
+}
 
+void KJavaDownloader::slotConnected(KIO::Job*)
+{
+    kdDebug(6100) << "slave connected" << endl;
+    d->responseCode = d->job->error();
+}
 
+void KJavaDownloader::slotMimetype(KIO::Job*, const QString & type) {
+    kdDebug(6100) << "slave mimetype " << type << endl;
 }
 
+#include <netinet/in.h>
 void KJavaDownloader::slotResult( KIO::Job* )
 {
     kdDebug(6100) << "slotResult for url = " << d->url->url() << endl;
 
-    if( d->job->error() )
+    if( d->job->error() || d->job->isErrorPage())
     {
         kdDebug(6100) << "slave had an error = " << d->job->errorString() << endl;
         KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
-        d->file.resize(0);
-        server->sendURLData( d->loaderID, d->url->url(), d->file );
+        d->file.resize(sizeof(int));
+        union {
+            char str[sizeof(int)];
+            int code;
+        } code;
+        code.code = htonl(d->job->error());
+        if (!code.code)
+            code.code = htonl(404);
+        memcpy( d->file.data(), code.str, sizeof(int) );
+
+        server->sendURLData( d->loaderID, RESPONSECODE, d->file );
         KJavaAppletServer::freeJavaServer();
     }
     else
@@ -95,7 +146,7 @@ void KJavaDownloader::slotResult( KIO::J
         kdDebug(6100) << "slave got all its data, sending to KJAS" << endl;
         kdDebug(6100) << "size of data = " << d->file.size() << endl;
         KJavaAppletServer* server = KJavaAppletServer::allocateJavaServer();
-        server->sendURLData( d->loaderID, d->url->url(), d->file );
+        server->sendURLData( d->loaderID, FINISHED, d->file );
         KJavaAppletServer::freeJavaServer();
     }
 
Index: kjavadownloader.h
===================================================================
RCS file: /home/kde/kdelibs/khtml/java/kjavadownloader.h,v
retrieving revision 1.3
diff -u -3 -p -r1.3 kjavadownloader.h
--- kjavadownloader.h	14 Feb 2001 18:24:01 -0000	1.3
+++ kjavadownloader.h	2 May 2003 22:02:24 -0000
@@ -41,11 +41,13 @@ class KJavaDownloader : public QObject
 Q_OBJECT
 
 public:
-	KJavaDownloader( QString& ID, QString& url );
-	~KJavaDownloader();
+    KJavaDownloader( int ID, const QString& url );
+    ~KJavaDownloader();
 
 protected slots:
     void slotData( KIO::Job*, const QByteArray& );
+    void slotConnected( KIO::Job* );
+    void slotMimetype( KIO::Job*, const QString& );
     void slotResult( KIO::Job* );
 
 private:
-------------- next part --------------
Index: KJASProtocolHandler.java
===================================================================
RCS file: /home/kde/kdelibs/khtml/java/org/kde/kjas/server/KJASProtocolHandler.java,v
retrieving revision 1.36
diff -u -3 -p -r1.36 KJASProtocolHandler.java
--- KJASProtocolHandler.java	7 Mar 2003 12:12:49 -0000	1.36
+++ KJASProtocolHandler.java	2 May 2003 21:57:15 -0000
@@ -230,27 +230,25 @@ public class KJASProtocolHandler
             Main.debug( "URLData recieved" );
             
             String loaderID = getArg( command );
-            String requestedURL = getArg( command );
+            String code = getArg( command );
             Main.debug( "data is for loader: " + loaderID );
-            Main.debug( "URL is " + requestedURL );
+            Main.debug( "Code is " + code );
 
             //rest of the command should be the data...
             byte[] data = new byte[ cmd_length - cmd_index ];
             System.arraycopy( command, cmd_index, data, 0, data.length );
-
-            KJASAppletClassLoader loader = KJASAppletClassLoader.getLoader( loaderID );
-            if( loader != null )
-            {
-                Main.info( "this is a class loader request and should not happen!" );
-                // loader.addResource( requestedURL, data );
-            }
-            else //see if there is a context with that ID, could be an image request
-            {
-                KJASAppletContext context = (KJASAppletContext) contexts.get( loaderID );
-                if( context != null )
-                {
-                    Main.debug( "this is  a context request for an image" );
-                    context.addImage( requestedURL, data );
+            synchronized (KJASHttpURLConnection.jobs) {
+                KJASHttpURLConnection job = (KJASHttpURLConnection)
+                                      KJASHttpURLConnection.jobs.get(loaderID);
+                if (job == null)
+                    Main.info("KJASHttpURLConnection already gone (timeout)");
+                else {
+                    job.setData(Integer.parseInt(code), data);
+                    if (job.thread != null) {
+                        try {
+                            job.thread.interrupt();
+                        } catch (SecurityException sex) {}
+                    }
                 }
             }
         } else
@@ -342,46 +340,31 @@ public class KJASProtocolHandler
     /**************************************************************
      *****  Methods for talking to the applet server **************
      **************************************************************/
-    public void sendGetURLDataCmd( String loaderID, String file )
+    public void sendGetURLDataCmd( String ID_str, String file )
     {
-        Main.info( "sendGetURLCmd from loader: " + loaderID + " url = " + file );
-        String ID_str = null;
-        String file_str = null;
-        try
-        {
-            ID_str = loaderID;
-            file_str = new URL( new URL(loaderID), file ).toString();
-        } catch( MalformedURLException e )
-        {
-            //this is an image request, take the file argument as is
-            ID_str = loaderID;
-            file_str = file;
-        }
-        finally
-        {
-            //length  = length of args plus 1 for code, 2 for seps and 1 for end
-            int length = ID_str.length() + file_str.length() + 4;
-            char[] chars = new char[ length + 8 ];
-            char[] tmpchar = getPaddedLength( length );
-            int index = 0;
-
-            System.arraycopy( tmpchar, 0, chars, index, tmpchar.length );
-            index += tmpchar.length;
-            chars[index++] = (char) GetURLDataCode;
-            chars[index++] = sep;
-
-                tmpchar = ID_str.toCharArray();
-            System.arraycopy( tmpchar, 0, chars, index, tmpchar.length );
-            index += tmpchar.length;
-            chars[index++] = sep;
-
-                tmpchar = file_str.toCharArray();
-            System.arraycopy( tmpchar, 0, chars, index, tmpchar.length );
-            index += tmpchar.length;
-            chars[index++] = sep;
+        Main.info( "sendGetURLCmd from : " + ID_str + " url = " + file );
+        //length  = length of args plus 1 for code, 2 for seps and 1 for end
+        int length = ID_str.length() + file.length() + 4;
+        char[] chars = new char[ length + 8 ];
+        char[] tmpchar = getPaddedLength( length );
+        int index = 0;
+
+        System.arraycopy( tmpchar, 0, chars, index, tmpchar.length );
+        index += tmpchar.length;
+        chars[index++] = (char) GetURLDataCode;
+        chars[index++] = sep;
+
+        tmpchar = ID_str.toCharArray();
+        System.arraycopy( tmpchar, 0, chars, index, tmpchar.length );
+        index += tmpchar.length;
+        chars[index++] = sep;
+
+        tmpchar = file.toCharArray();
+        System.arraycopy( tmpchar, 0, chars, index, tmpchar.length );
+        index += tmpchar.length;
+        chars[index++] = sep;
 
-            signals.print( chars );
-        }
+        signals.print( chars );
     }
 
     /**
Index: Main.java
===================================================================
RCS file: /home/kde/kdelibs/khtml/java/org/kde/kjas/server/Main.java,v
retrieving revision 1.34
diff -u -3 -p -r1.34 Main.java
--- Main.java	18 Feb 2003 23:20:11 -0000	1.34
+++ Main.java	2 May 2003 21:57:15 -0000
@@ -125,6 +125,7 @@ public class Main
         if( show_console )
             console.setVisible( true );
 
+        java.net.URL.setURLStreamHandlerFactory(new KJASURLStreamHandlerFactory());
         // set up https
         boolean hasHTTPS = true;
 


More information about the kfm-devel mailing list