Author: bdonlan
Date: 2005-05-23 17:06:31 -0400 (Mon, 23 May 2005)
New Revision: 713

Added:
   trunk/clients/Javer2/src/org/
   trunk/clients/Javer2/src/org/haverdev/
   trunk/clients/Javer2/src/org/haverdev/haver/
   trunk/clients/Javer2/src/org/haverdev/haver/Callback.java
   trunk/clients/Javer2/src/org/haverdev/haver/Client.java
   trunk/clients/Javer2/src/org/haverdev/haver/NonblockingOutputStream.java
   trunk/clients/Javer2/src/org/haverdev/haver/SHA1.java
   trunk/clients/Javer2/src/org/haverdev/javer2/
   trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java
   trunk/clients/Javer2/src/org/haverdev/javer2/Main.java
Modified:
   trunk/clients/Javer2/nbproject/project.properties
Log:
Use org.haverdev.*


Modified: trunk/clients/Javer2/nbproject/project.properties
===================================================================
--- trunk/clients/Javer2/nbproject/project.properties   2005-05-23 20:58:15 UTC 
(rev 712)
+++ trunk/clients/Javer2/nbproject/project.properties   2005-05-23 21:06:31 UTC 
(rev 713)
@@ -36,7 +36,7 @@
 javadoc.use=true
 javadoc.version=false
 javadoc.windowtitle=
-main.class=javer2.CMod
+main.class=org.haverdev.javer2.CMod
 manifest.file=manifest.mf
 platform.active=Java_HotSpot_TM__Client_VM_1.4.2_08-b03
 run.classpath=\

Added: trunk/clients/Javer2/src/org/haverdev/haver/Callback.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/Callback.java   2005-05-23 
20:58:15 UTC (rev 712)
+++ trunk/clients/Javer2/src/org/haverdev/haver/Callback.java   2005-05-23 
21:06:31 UTC (rev 713)
@@ -0,0 +1,84 @@
+/*
+ * Callback.java
+ *
+ * Created on May 21, 2005, 9:39 PM
+ */
+
+package org.haverdev.haver;
+import java.io.IOException;
+
+/**
+ *
+ * @author bdonlan
+ */
+public abstract class Callback implements java.util.EventListener {
+    /**
+     * Called when a connection to the server is established but before 
handshaking
+     * occurs.
+     * @param source haver.Client instance associated with this event
+     * @throws java.io.IOException 
+     */
+    public void onConnect(Client source) throws Throwable {}
+    /**
+     * Called when a connection attempt fails.
+     * @param source haver.Client instance associated with this event
+     * @param e IOException which caused the connection failure
+     * @throws java.io.IOException 
+     */
+    public void onConnectFailed(Client source, java.io.IOException e) throws 
Throwable {}
+    /**
+     * Called when an established connection is lost
+     * @param source haver.Client instance associated with this event
+     * @param e Exception associated with the connection loss, or null if 
connection loss did
+     * not have an associated exception (e.g., connection broken)
+     * @throws java.io.IOException 
+     */
+    public void onDisconnected(Client source, java.io.IOException e) throws 
Throwable {}
+    /**
+     * Called when a message is received from the server
+     * @param source haver.Client instance associated with this event
+     * @param args Arguments of the message
+     * @throws java.io.IOException 
+     */
+    public void onIncomingLine(Client source, String[] args) throws Throwable 
{}
+    /**
+     * Called when a message is sent to the server
+     * @param source haver.Client instance associated with this event
+     * @param args Arguments of the message
+     * @throws java.io.IOException 
+     */
+    public void onOutgoingLine(Client source, String[] args) throws Throwable 
{}    
+    /**
+     * Called when the server requires identification
+     * @param source haver.Client instance associated with this event
+     * @throws java.io.IOException 
+     */
+    public void onNeedIdent(Client source) throws Throwable {}
+    /**
+     * Called when the name given by the client has been accepted
+     * @param source haver.Client instance associated with this event
+     * @param name Name that was accepted by the server
+     * @throws java.io.IOException 
+     */
+    public void onAccept(Client source, String name) throws Throwable {}
+    
+    /**
+     * Called when the server responds to a listing request
+     * @param source haver.Client instance associated with this event
+     * @param channel Channel that was listed
+     * @param namespace Namespace of objects listed
+     * @param list List of objects held by the channel
+     * @throws java.io.IOException 
+     */
+    public void onReceivedList(Client source, String channel, String 
namespace, String[] list) throws Throwable {}
+    
+    public void onPublicMessage(Client source, String channel, String from, 
String type, String[] args) throws Throwable {}
+    
+    public void onPrivateMessage(Client source, String from, String type, 
String[] args) throws Throwable {}
+    
+    public void onJoin(Client source, String channel, String who) throws 
Throwable {}
+    
+    public void onPart(Client source, String channel, String who) throws 
Throwable {}
+    
+    public void onQuit(Client source, String who, String type, String detail) 
throws Throwable {}
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/haver/Callback.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/haver/Client.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/Client.java     2005-05-23 
20:58:15 UTC (rev 712)
+++ trunk/clients/Javer2/src/org/haverdev/haver/Client.java     2005-05-23 
21:06:31 UTC (rev 713)
@@ -0,0 +1,837 @@
+/*
+ * Client.java
+ *
+ * Created on May 21, 2005, 8:48 PM
+ */
+
+package org.haverdev.haver;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+import java.lang.reflect.*;
+
+/**
+ * A Haver client class
+ * @author bdonlan
+ */
+public class Client {
+    
+    HashSet listening = new java.util.HashSet();
+    CallbackDist dist;
+    Socket theSocket = null;
+    boolean connecting = false;
+    Thread io = null;
+    Client self = this;
+    PrintWriter writer = null;
+    BufferedReader reader = null;
+    NonblockingOutputStream s;
+    String name = null;
+    
+    static final String[] greeting = {"HAVER", "haver.Client/0.00"};
+    
+    String host;
+    int port;
+    
+    /**
+     * Mutable boolean flag
+     */
+    class Flag {
+        public boolean ok = true;
+    }
+    
+    /** Creates a new instance of Client */
+    public Client() {
+        dist = new CallbackDist(listening);
+    }
+
+    /**
+     * Reverses Haver escaping.
+     * @param val String to unescape
+     * @return The unescaped string
+     */
+    protected static final String unescape(String val) {
+        //if (val != null) return val;
+        StringBuffer out = new StringBuffer();
+        int pin = 0;
+        int nextEsc = -1;
+        while (-1 != (nextEsc = val.indexOf("\033", pin))) {
+            out.append(val.substring(0, nextEsc - 1));
+            switch (val.charAt(nextEsc + 1)) {
+                case 'r':
+                    out.append('\r');
+                    break;
+                case 'n':
+                    out.append('\n');
+                    break;
+                case 'e':
+                    out.append('\033');
+                    break;
+                case 't':
+                    out.append('\t');
+                    break;
+                default:
+                    out.append(val.charAt(nextEsc + 1));
+                    break;
+            }
+            pin = nextEsc + 2;
+        }
+        out.append(val.substring(pin));
+        return out.toString();
+    }
+    
+    /**
+     * Escapes a string according to the Haver protocol
+     * @param val String to escape
+     * @return Escaped string
+     */
+    protected static final String escape(String val) {
+        return val.replaceAll("\033", "\033e")
+               .replaceAll("\n", "\033n")
+               .replaceAll("\r", "\033r")
+               .replaceAll("\t", "\033t");
+    }
+    
+    /**
+     * Decodes a raw line from the Haver protocol.
+     * @param inLine Line to decode. May end in '\r\n' but must only be one 
line.
+     * @return An array containing the unescaped arguments of the line.
+     */
+    protected static final String[] decodeLine(String inLine) {
+        inLine = inLine.replaceAll("[\n\r]", ""); // We'll assume you only 
passed one
+                                            // line in.
+        String[] args = inLine.split("\t");
+        for (int i = 0; i < args.length; i++) {
+            args[i] = unescape(args[i]);
+        }
+        return args;
+    }
+    
+    /**
+     * Encodes a set of arguments for line transmission
+     * @param args Unescaped arguments to encode
+     * @return Escaped and joined line ready for transmission, complete with 
ending \r\n
+     */
+    protected static final String encodeLine(String[] args) {
+        StringBuffer out = new StringBuffer();
+        for (int i = 0; i < args.length; i++) {
+            if (i != 0)
+                out.append("\t");
+            out.append(escape(args[i]));
+        }
+        out.append("\r\n");
+        return out.toString();
+    }
+
+    /**
+     * Internal class for distributing callback events to all listeners
+     */
+    protected class CallbackDist extends Callback {
+        java.util.HashSet listeners;
+        
+        /**
+         * 
+         * @param l Listener set
+         */
+        public CallbackDist(HashSet l) {
+            listeners = l;
+        }
+        
+        /**
+         * Returns a constant iterator over the current listeners, which will 
not be
+         * affected by concurrency issues.
+         */
+        protected Iterator iterator() {
+            // Prevent concurrency issues
+            synchronized (listeners) {
+                return ((HashSet)listeners.clone()).iterator();
+            }
+        }
+        
+        public void onDisconnected(Client source, IOException e) throws 
IOException {
+            try {reader.close();} catch (Throwable t) {}
+            try {writer.close();} catch (Throwable t) {}
+            try {theSocket.close();} catch (Throwable t) {}
+            reader = null;
+            writer = null;
+            theSocket = null;
+            io = null;
+            
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onDisconnected(source, e);
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+
+        }
+
+        public void onConnectFailed(Client source, IOException e) throws 
IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onConnectFailed(source, e);
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+        }
+
+        public void onConnect(Client source) throws IOException {
+            // Konnichiha, saaba-san!
+            sendLine(greeting);
+            
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onConnect(source);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onOutgoingLine(Client source, String[] args) throws 
IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onOutgoingLine(source, args);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onIncomingLine(Client source, String[] args) throws 
IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onIncomingLine(source, args);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onNeedIdent(Client source) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onNeedIdent(source);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onAccept(Client source, String name) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onAccept(source, name);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onReceivedList(Client source, String channel, String 
namespace, String[] list) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onReceivedList(source, channel, namespace, list);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onPublicMessage(Client source, String channel, String 
from, String type, String[] args) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onPublicMessage(source, channel, from, type, args);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onPrivateMessage(Client source, String from, String type, 
String[] args) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onPrivateMessage(source, from, type, args);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onQuit(Client source, String who, String type, String 
detail) throws IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onQuit(source, who, type, detail);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onPart(Client source, String channel, String who) throws 
IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onPart(source, channel, who);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void onJoin(Client source, String channel, String who) throws 
IOException {
+            Iterator i = iterator();
+            while (i.hasNext()) {
+                Callback c = (Callback)i.next();
+                try {
+                    c.onJoin(source, channel, who);
+                } catch (IOException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+      
+    }
+    
+    /**
+     * Entry point for IO thread; connects to specified host and port.
+     * @param host Hostname to connect to
+     * @param port Port number to connect to
+     */
+    protected void run(String host, int port) {
+        synchronized (this) {
+            if (Thread.currentThread() != io) {
+                throw new IllegalStateException("Client.run called outside io 
thread");
+            }
+        }
+        Thread.currentThread().setName("haver.Client input thread");
+        try {
+            Socket mySock = new Socket(host, port);
+            theSocket = mySock;
+        } catch (IOException e) {
+            try {
+                io = null;
+                dist.onConnectFailed(this, e);
+            } catch (IOException e2) {}
+            return;
+        }
+        try {
+
+            writer = new PrintWriter
+                        (new OutputStreamWriter
+                        (s = new NonblockingOutputStream
+                        (new BufferedOutputStream
+                        (theSocket.getOutputStream()))));
+            s.setAutoFlush(true);
+            reader = new BufferedReader
+                    (new InputStreamReader
+                    (theSocket.getInputStream()));
+            dist.onConnect(this);
+            ioLoop(new Flag());
+        } catch (IOException e) {
+            try {
+                dist.onDisconnected(this, e);
+            } catch (IOException e2) {
+            }
+            return;
+        }
+    }
+    
+    /**
+     * Sends a command to the server.
+     * @param args Arguments of the line to send
+     * @throws java.io.IOException 
+     */
+    protected synchronized void sendLine(String[] args) throws IOException {
+        if (writer != null) {
+            String l = encodeLine(args);
+            System.out.print("C: " + l);
+            writer.print(l);
+            writer.flush();
+        }
+    }
+    
+    protected synchronized void killConnection() {
+        // Kill the connection. Don't ask questions, just do it.
+        try {
+            writer.flush();
+        } catch (Throwable t) {}
+        try {
+            theSocket.close();
+        } catch (Throwable t) {}
+    }
+    
+    /**
+     * Main IO loop
+     * @param f Termination flag; ioLoop will return when f.ok is false
+     * @throws java.io.IOException 
+     */
+    protected void ioLoop(Flag f) throws IOException {
+        while (io != null && f.ok) {
+            String l = reader.readLine();
+            if (l == null) {
+                dist.onDisconnected(this, null);
+                return;
+            }
+            System.out.println("S: " + l);
+            String[] args = decodeLine(l);
+            dist.onIncomingLine(this, args);
+            dispatch(args);
+        }
+    }
+    
+    /**
+     * Connect to a given host asynchronously.
+     * @param host Hostname to connect to
+     * @param port Port number to connect to
+     * @throws java.lang.IllegalStateException Thrown if the client is already 
connected or connecting.
+     */
+    public synchronized void connect(String host, int port)
+            throws IllegalStateException
+    {
+        if (io != null && io.isAlive()) {
+            throw new IllegalStateException("Already connecting");
+        }
+        name = null;
+        theSocket = null;
+        this.host = host;
+        this.port = port;
+        io = new Thread(new Runnable() {
+            public void run() {
+                self.run(self.host, self.port);
+            }
+        });
+        io.start();
+    }
+    
+    /**
+     * Base class for event listeners for synchronous methods
+     */
+    protected class SyncMonitor extends Callback {
+        /**
+         * The exception which caused the operation to fail, or null if none
+         */
+        private IOException fail = null;
+        /**
+         * Flag for ioLoop() termination
+         */
+        private Flag flag = new Flag();
+        
+        /**
+         * Return value
+         */
+        private Object ret = null;
+        
+        public SyncMonitor() {
+            addNotify(this);
+        }
+        
+        /**
+         * Signal completion of the synchronous event
+         */
+        protected synchronized void done(Object ret) {
+            if (!flag.ok)
+                throw new IllegalStateException("Already finished!");
+            removeNotify(this);
+            flag.ok = false;
+            this.ret = ret;
+            notifyAll();
+        }
+        
+        public synchronized void onDisconnected(Client source, IOException e) {
+            if (e == null)
+                e = new IOException("Disconnected unexpectedly");
+            fail = e;
+            done(null);
+        }
+        
+        public synchronized void onConnectFailed(Client source, IOException e) 
{
+            fail = e;
+            done(null);
+        }
+        
+        /**
+         * Wait until the synchronous event completes
+         * @throws java.io.IOException 
+         */
+        public synchronized Object block() throws IOException, 
InterruptedException {
+            if (Thread.currentThread() == io) {
+                ioLoop(flag);
+            } else {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    removeNotify(this);
+                    throw e;
+                }
+            }
+            if (fail != null)
+                throw fail;
+            return ret;
+        }
+    }
+    
+    /**
+     * Event listener for syncConnect()
+     */
+    protected class ConnectMonitor extends SyncMonitor {
+        /**
+         * true if connection completed successfully
+         */
+        public boolean done = false;
+        /**
+         * Name for login
+         */
+        public String name;
+        
+        public ConnectMonitor(String name) {
+            this.name = name;
+        }
+        
+        public void onNeedIdent(Client source) throws IOException {
+            source.ident(name);
+        }
+
+        public synchronized void onAccept(Client source, String name) {
+            done(null);
+        }
+    }
+    
+    /**
+     * Connects to a host synchronously.
+     * @param host Hostname to connect to.
+     * @param port Port to connect to.
+     * @param name Name to use upon connect.
+     * @throws java.io.IOException Thrown if an IO exception occurs at any 
point during the connect, or if the
+     * connection is lost.
+     */
+    public void syncConnect(String host, int port, String name) throws 
IOException, InterruptedException {
+        ConnectMonitor m = new ConnectMonitor(name);
+        connect(host, port);
+        m.block();
+    }
+    
+    /**
+     * Adds a callback to notify when an event occurs
+     * @param c Callback class to add
+     */
+    public void addNotify(Callback c) {
+        synchronized (listening) {
+            listening.add(c);
+        }
+    }
+    
+    /**
+     * Removes a callback class from the notify list
+     * @param c Callback to remove
+     */
+    public void removeNotify(Callback c) {
+        synchronized (listening) {
+            listening.remove(c);
+        }
+    }
+    
+    /**
+     * Dispatch the passed line to the appropriate internal handler (if 
present)
+     * @param args Arguments of line, including command.
+     * @throws java.io.IOException 
+     */
+    protected void dispatch(String[] args) throws IOException {
+        String cmd = args[0].toUpperCase();
+        Class me = this.getClass();
+        try {
+            Class[] params = {args.getClass()};
+            Method m = me.getDeclaredMethod("handle_" + cmd, params);
+            Object[] args2 = {args};
+            m.invoke(this, args2);
+        } catch (InvocationTargetException e) {
+            Throwable thingy = e.getTargetException();
+            if (thingy instanceof IOException) {
+                throw (IOException) thingy;
+            }
+            thingy.printStackTrace();
+        }
+        catch (NoSuchMethodException e) {} // unhandled server event
+        catch (IllegalAccessException e) {
+            // eep!
+            e.printStackTrace();
+        }
+    }
+    
+    /**
+     * Handler for server HAVER message
+     * @param args Arguments of message
+     * @throws java.io.IOException 
+     */
+    protected void handle_HAVER(String[] args) throws IOException {
+        dist.onNeedIdent(this);
+    }
+    
+    /**
+     * Identify with the given name
+     * @param name Name to identify with
+     * @throws java.io.IOException 
+     */
+    public void ident(String name) throws IOException {
+        String[] l = {"IDENT", name};
+        sendLine(l);
+    }
+    
+    /**
+     * Handler for server HELLO message
+     * @param args Arguments of message
+     * @throws java.io.IOException 
+     */
+    protected void handle_HELLO(String[] args) throws IOException {
+        name = args[1];
+        dist.onAccept(this, args[1]);
+    }
+    
+    /**
+     * Handler for server PING message
+     * @param args Arguments of message
+     * @throws java.io.IOException 
+     */
+    protected void handle_PING(String[] args) throws IOException {
+        args[0] = "PONG";
+        sendLine(args);
+    }
+    
+    /**
+     * Request a listing of the contents of a channel, asynchronously.
+     * @param channel Channel to list
+     * @param namespace Namespace to filter results by
+     * @throws java.io.IOException 
+     */
+    public void requestList(String channel, String namespace) throws 
IOException {
+        String[] cmd = {"LIST", channel, namespace};
+        sendLine(cmd);
+    }
+    
+    /**
+     * Handler for server LIST message
+     * @param args Arguments of message
+     * @throws java.io.IOException 
+     */
+    protected void handle_LIST(String[] args) throws IOException {
+        String[] list = new String[args.length - 3];
+        System.arraycopy(args, 3, list, 0, args.length - 3);
+        String channel = args[1];
+        String namespace = args[2];
+        
+        dist.onReceivedList(this, channel, namespace, list);
+    }
+    
+    /**
+     * Event listener for syncList()
+     */
+    protected class ListMonitor extends SyncMonitor {
+        String channel;
+        String namespace;
+        
+        public ListMonitor(String channel, String namespace) {
+            this.channel = channel;
+            this.namespace = namespace;
+        }
+
+        public synchronized void onReceivedList(Client source, String channel, 
String namespace, String[] list) {
+            if (channel.equals(this.channel) &&
+                namespace.equals(this.namespace))
+            {
+                done(list);
+            }
+        }
+    }
+    
+    /**
+     * Synchronously list a channel.
+     * @param channel Channel to list
+     * @param namespace Namespace to restrict results by
+     * @throws java.io.IOException If an IO exception occurs while this 
function is being processed, it will be
+     * rethrown from here.
+     * @return An array containing the elements of the given namespace, in the 
specified
+     * channel
+     */
+    public String[] syncList(String channel, String namespace) throws 
IOException, InterruptedException {
+        ListMonitor m = new ListMonitor(channel, namespace);
+        requestList(channel, namespace);
+        return (String[])m.block();
+    }
+    
+    protected void handle_IN(String[] args) throws IOException {
+        if (args.length < 4) {
+            // XXX: exception?
+            System.err.println("Short IN message from haver server");
+            return;
+        }
+        String channel = args[1];
+        String from = args[2];
+        String type = args[3];
+        String[] margs = new String[args.length - 4];
+        System.arraycopy(args, 4, margs, 0, args.length - 4);
+        
+        dist.onPublicMessage(this, channel, from, type, margs);
+    }
+    
+    public void sendPublicMessage(String channel, String type, String[] margs) 
throws IOException {
+        String[] cmd = new String[margs.length + 3];
+        cmd[0] = "IN";
+        cmd[1] = channel;
+        cmd[2] = type;
+        System.arraycopy(margs, 0, cmd, 3, margs.length);
+        sendLine(cmd);
+    }
+    
+    public void sendPublicMessage(String channel, String type, String arg) 
throws IOException {
+        String[] args = {arg};
+        sendPublicMessage(channel, type, args);
+    }
+    
+    protected void handle_FROM(String[] args) throws IOException {
+        if (args.length < 3) {
+            // XXX: exception?
+            System.err.println("Short FROM message from haver server");
+            return;
+        }
+        String user = args[1];
+        String type = args[2];
+        String margs[] = new String[args.length - 3];
+        System.arraycopy(args, 3, margs, 0, margs.length);
+        
+        dist.onPrivateMessage(this, user, type, margs);
+    }
+    
+    public void sendPrivateMessage (String target, String type, String[] 
margs) throws IOException {
+        String[] args = new String[margs.length + 3];
+        args[0] = "TO";
+        args[1] = target;
+        args[2] = type;
+        System.arraycopy(margs, 0, args, 3, margs.length);
+        sendLine(args);
+    }
+    
+    public void sendPrivateMessage (String target, String type, String arg) 
throws IOException {
+        String[] args = {arg};
+        sendPrivateMessage(target, type, args);
+    }
+    
+    public void handle_JOIN(String[] args) throws IOException {
+        String channel = args[1];
+        String user = args[2];
+        dist.onJoin(this, channel, user);
+    }
+    
+    public void handle_PART(String[] args) throws IOException {
+        String channel = args[1];
+        String user = args[2];
+        dist.onPart(this, channel, user);
+    }
+    
+    public void handle_QUIT(String[] args) throws IOException {
+        String who = args[1];
+        String type = args[2];
+        String detail = args[3];
+        dist.onQuit(this, who, type, detail);
+    }
+    
+    public void sendJoin(String channel) throws IOException {
+        String[] cmd = {"JOIN", channel};
+        sendLine(cmd);
+    }
+    
+    protected class JoinMonitor extends SyncMonitor {
+        String channel;
+        
+        public JoinMonitor (String channel) {
+            this.channel = channel;
+        }
+        
+        public void onJoin(Client from, String channel, String who) {
+            if (channel.equals(this.channel) && who.equals(name)) {
+                this.done(null);
+            }
+        }
+    }
+    
+    public void syncJoin(String channel) throws IOException, 
InterruptedException {
+        JoinMonitor m = new JoinMonitor(channel);
+        sendJoin(channel);
+        m.block();
+    }
+    
+    public void sendPart(String channel) throws IOException {
+        String[] cmd = {"PART", channel};
+        sendLine(cmd);
+    }
+
+    protected class PartMonitor extends SyncMonitor {
+        String channel;
+        
+        public PartMonitor (String channel) {
+            this.channel = channel;
+        }
+        
+        public void onPart(Client from, String channel, String who) {
+            if (channel.equals(this.channel) && who.equals(name)) {
+                this.done(null);
+            }
+        }
+    }
+    
+    public void syncPart(String channel) throws IOException, 
InterruptedException {
+        PartMonitor m = new PartMonitor(channel);
+        sendPart(channel);
+        m.block();
+    }
+    
+    public synchronized void disconnect() {
+        killConnection();
+    }
+    
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/haver/Client.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/haver/NonblockingOutputStream.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/NonblockingOutputStream.java    
2005-05-23 20:58:15 UTC (rev 712)
+++ trunk/clients/Javer2/src/org/haverdev/haver/NonblockingOutputStream.java    
2005-05-23 21:06:31 UTC (rev 713)
@@ -0,0 +1,298 @@
+/*
+ * NonblockingOutputStream.java
+ *
+ * Created on January 10, 2005, 1:53 PM
+ */
+
+package org.haverdev.haver;
+import java.io.*;
+import java.util.*;
+
+/**
+ * A wrapper over OutputStream to prevent any methods from blocking, by using a
+ * worker thread to drive the inner stream.
+ * @author bdonlan
+ */
+public class NonblockingOutputStream extends java.io.FilterOutputStream
+    implements Runnable {
+
+    /**
+     * Pending data for after the flush
+     */
+    protected Vector preflush;
+
+    /**
+     * If a flush is pending, all data included in that flush and not yet 
owned by the
+     * worker thread. If no flush is pending, all data.
+     */
+    protected Vector pending;
+    /**
+     * Length of data currently owned by the write thread, in bytes
+     */
+    protected long active_len;
+    /**
+     * Length of data in the pending vector, in bytes
+     */
+    protected long pending_len;
+    /**
+     * Length of data in the preflush vector, in bytes
+     */
+    protected long preflush_len;
+
+    /**
+     * A manual flush is pending
+     */
+    protected boolean flushing;
+
+    /**
+     * The writer is in the process of closing
+     */
+    protected boolean closing;
+
+    /**
+     * Whether autoflush is enabled.
+     */
+    protected boolean autoflush;
+    /**
+     * The pending write exception, if any
+     */
+    protected IOException pending_exception = null;
+    /**
+     * The worker thread
+     */
+    protected Thread th;
+    /**
+     * The wrapped output stream
+     */
+    protected OutputStream out;
+    
+    /**
+     * Create a new instance of NonblockingOutputStream
+     * @param out OutputStream to wrap
+     */
+    public NonblockingOutputStream(OutputStream out) {
+        super(out);
+        this.out = out;
+        flushing = closing = autoflush = false;
+        preflush = null;
+        pending = new Vector();
+        active_len = pending_len = preflush_len = 0;
+        th = new Thread(this);
+        th.setDaemon(true);
+        th.start();
+    }
+    
+    /**
+     * Worker thread entry point
+     */
+    public void run() {
+        if (Thread.currentThread() != th)
+            throw new IllegalStateException("run() must be executed from 
within " +
+                    "NonblockingOutputStream.th");
+        th.setName("NonblockingOutputStream worker thread");
+        try {
+            while (true) {
+                Vector work;
+                boolean flushafter = false;
+                synchronized (th) {
+                    active_len = 0;
+                    if (flushing) {
+                        flushafter = true;
+                        flushing = false;
+
+                    }
+                    work = pending;
+                    pending = new Vector();
+                    active_len = pending_len;
+                    pending_len = 0;
+                    if (flushing) {
+                        flushafter = true;
+                        flushing = false;
+                        pending = preflush;
+                        pending_len = preflush_len;
+                        preflush_len = 0;
+                        preflush = null;
+                    } else if (active_len == 0) {
+                        //System.out.println("active_len == 0, blocking");
+                        if (autoflush) {
+                            out.flush();
+                        }
+                        if (closing) {
+                            out.close();
+                            return;
+                        }
+                        try {
+                            th.wait();
+                        } catch (InterruptedException e) {}
+                        //System.out.println("done blocking");
+                        continue;
+                    }
+                }
+                Iterator it;
+                //System.out.println("writing, active_len = " + active_len);
+                it = work.iterator();
+                while (it.hasNext()) {
+                    byte[] b = (byte[]) it.next();
+                    out.write(b);
+                }
+                if (flushafter)
+                    out.flush();
+            }
+        } catch (IOException e) {
+            synchronized(th) {
+                pending_exception = e;
+            }
+        }
+    }
+    
+    /**
+     * Writes <code>b.length</code> bytes to this output stream. 
+     * 
+     * @param      b   the data to be written.
+     * @exception  IOException  if an I/O error occurs.
+     */
+    public void write(byte[] b) throws IOException {
+        if (b.length == 0)
+            return;
+        byte[] bc = new byte[b.length];
+        System.arraycopy(b, 0, bc, 0, b.length);
+        write_(bc);
+    }
+    
+    protected void write_(byte[] b) throws IOException {
+        synchronized(th) {
+            if (pending_exception != null)
+                throw pending_exception;
+            if (flushing) {
+                preflush.add(b);
+                preflush_len += b.length;
+            } else {
+                pending.add(b);
+                pending_len += b.length;
+                th.notifyAll();
+            }
+            dump();
+        }
+    }
+    
+    /**
+     * Closes this output stream and releases any system resources 
+     * associated with the stream. 
+     * <p>
+     * This method returns immediately. After it returns no more methods of 
this
+     * instance may be called. Any pending data will be written to the
+     * underlying <code>OutputStream</code> before the worker thread is shut
+     * down.
+     * 
+     * @exception  IOException  if an I/O error occurs.
+     * @see        java.io.FilterOutputStream#flush()
+     * @see        java.io.FilterOutputStream#out
+     */
+    public void close() throws IOException {
+        synchronized(th) {
+            if (pending_exception != null)
+                throw pending_exception;
+            closing = true;
+            th.notifyAll();
+            dump();
+        }
+    }
+
+    /**
+     * Flushes this output stream and forces any buffered output bytes 
+     * to be written out to the stream. 
+     * <p>
+     * This method returns immediately, without waiting for the flush to
+     * complete.
+     * 
+     * @exception  IOException  if an I/O error occurs.
+     */
+    public void flush() throws IOException {
+        synchronized(th) {
+            if (pending_exception != null)
+                throw pending_exception;
+            /* if we'return autoflushing, eventually a buffer will fill 
somewhere
+             * and cause a flush, or else autoFlush will kick in. So the manual
+             * flush() is redundant
+             */
+            if (autoflush)
+                return;
+            flushing = true;
+            preflush = new Vector();
+            preflush_len = 0;
+            th.notifyAll();
+            dump();
+        }
+    }
+  
+    /**
+     * Returns whether the underlying <code>OutputStream</code> will be 
flushed when
+     * pending data runs out.
+     * @return Whether the underlying <code>OutputStream</code> will be 
flushed when pending
+     * data runs out.
+     */
+    public boolean isAutoFlush() {
+        return autoflush;
+    }
+
+    /**
+     * Sets whether the underlying <code>OutputStream</code> should be flushed
+     * whenever pending data runs out.
+     * @param autoFlush Whether the underlying <code>OutputStream</code> 
should be flushed when pending
+     * data runs out.
+     */
+    public void setAutoFlush(boolean autoFlush) {
+        synchronized (th) {
+            autoflush = true;
+            th.notify();
+        }
+    }
+    
+    protected void dump() {
+        /*
+        System.out.println("=== NonblockingOutputStream status:");
+        synchronized(th) {
+            System.out.println("Flags: flushing="+flushing+" 
closing="+closing+" autoflush="+autoflush);
+            System.out.println("Buffer levels: preflush_len=" + preflush_len + 
" pending_len="+pending_len);
+            System.out.print("Vector element counts: ");
+            if (preflush == null) {
+                System.out.print("preflush={null}" );
+            } else {
+                System.out.print("preflush=" + preflush.size());
+            }
+            System.out.println("pending=" + pending.size());
+        }
+        System.out.println("Stack trace:");
+        new Exception().printStackTrace();
+         */
+    }
+
+    /**
+     * Writes the specified <code>byte</code> to this output stream. 
+     * 
+     * @param      b   the <code>byte</code>.
+     * @exception  IOException  if an I/O error occurs.
+     */
+
+    public void write(int b) throws IOException {
+        byte[] a = {(byte)b};
+        write_(a);
+    }
+    
+    /**
+     * Writes <code>len</code> bytes from the specified 
+     * <code>byte</code> array starting at offset <code>off</code> to 
+     * this output stream. 
+     * 
+     * @param      b     the data.
+     * @param      off   the start offset in the data.
+     * @param      len   the number of bytes to write.
+     * @exception  IOException  if an I/O error occurs.
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        byte[] buffer = new byte[len];
+        System.arraycopy(b, off, buffer, 0, len);
+        write_(buffer);
+    }
+
+}


Property changes on: 
trunk/clients/Javer2/src/org/haverdev/haver/NonblockingOutputStream.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/haver/SHA1.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/haver/SHA1.java       2005-05-23 
20:58:15 UTC (rev 712)
+++ trunk/clients/Javer2/src/org/haverdev/haver/SHA1.java       2005-05-23 
21:06:31 UTC (rev 713)
@@ -0,0 +1,697 @@
+package org.haverdev.haver;
+/* @(#)SHA1.java       1.11 2004-04-26
+ * This file was freely contributed to the LimeWire project and is covered
+ * by its existing GPL licence, but it may be used individually as a public
+ * domain implementation of a published algorithm (see below for references).
+ * It was also freely contributed to the Bitzi public domain sources.
+ * @author  Philippe Verdy
+ */
+ 
+/* Sun may wish to change the following package name, if integrating this
+ * class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger).
+ *
+ * You can include it in your own Security Provider by inserting
+ * this property in your Provider derived class:
+ * put("MessageDigest.SHA-1", "com.bitzi.util.SHA1");
+ */
+//package com.bitzi.util;
+import java.security.*;
+//--+---+1--+---+--2+---+---+3--+---+--4+---+---+5--+---+--6+---+---+7--+---+--
+//34567890123456789012345678901234567890123456789012345678901234567890123456789
+ 
+/**
+ * <p>The FIPS PUB 180-2 standard specifies four secure hash algorithms (SHA-1,
+ * SHA-256, SHA-384 and SHA-512) for computing a condensed representation of
+ * electronic data (message).  When a message of any length < 2^^64 bits (for
+ * SHA-1 and SHA-256) or < 2^^128 bits (for SHA-384 and SHA-512) is input to
+ * an algorithm, the result is an output called a message digest.  The message
+ * digests range in length from 160 to 512 bits, depending on the algorithm.
+ * Secure hash algorithms are typically used with other cryptographic
+ * algorithms, such as digital signature algorithms and keyed-hash message
+ * authentication codes, or in the generation of random numbers (bits).</p>
+ *
+ * <p>The four hash algorithms specified in this "SHS" standard are called
+ * secure because, for a given algorithm, it is computationally infeasible
+ * 1) to find a message that corresponds to a given message digest, or 2)
+ * to find two different messages that produce the same message digest.  Any
+ * change to a message will, with a very high probability, result in a
+ * different message digest.  This will result in a verification failure when
+ * the secure hash algorithm is used with a digital signature algorithm or a
+ * keyed-hash message authentication algorithm.</p>
+ *
+ * <p>A "SHS change notice" adds a SHA-224 algorithm for interoperability,
+ * which, like SHA-1 and SHA-256, operates on 512-bit blocks and 32-bit words,
+ * but truncates the final digest and uses distinct initialization values.</p>
+ *
+ * <p><b>References:</b></p>
+ * <ol>
+ *   <li> NIST FIPS PUB 180-2, "Secure Hash Signature Standard (SHS) with
+ *      change notice", National Institute of Standards and Technology (NIST),
+ *      2002 August 1, and U.S. Department of Commerce, August 26.<br>
+ *      <a href="http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html";>
+ *      http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html</a>
+ *   <li> NIST FIPS PUB 180-1, "Secure Hash Standard",
+ *      U.S. Department of Commerce, May 1993.<br>
+ *      <a href="http://www.itl.nist.gov/div897/pubs/fip180-1.htm";>
+ *      http://www.itl.nist.gov/div897/pubs/fip180-1.htm</a></li>
+ *   <li> Bruce Schneier, "Section 18.7 Secure Hash Algorithm (SHA)",
+ *      <cite>Applied Cryptography, 2nd edition</cite>, <br>
+ *      John Wiley & Sons, 1996</li>
+ * </ol>
+ */
+public final class SHA1 extends MessageDigest implements Cloneable {
+ 
+    /**
+     * This implementation returns a fixed-size digest.
+     */
+    private static final int HASH_LENGTH = 20; // bytes == 160 bits
+ 
+    /**
+     * Private context for incomplete blocks and padding bytes.
+     * INVARIANT: padding must be in 0..63.
+     * When the padding reaches 64, a new block is computed, and
+     * the 56 last bytes are kept in the padding history.
+     */
+    private byte[] pad;
+    private int padding;
+ 
+    /**
+     * Private contextual byte count, sent in the next block,
+     * after the ending padding block.
+     */
+    private long bytes;
+ 
+    /**
+     * Private context that contains the current digest key.
+     */
+    private int hA, hB, hC, hD, hE;
+ 
+    /**
+     * Creates a SHA1 object with default initial state.
+     */
+    public SHA1() {
+        super("SHA-1");
+        pad = new byte[64];
+        init();
+    }
+ 
+    /**
+     * Clones this object.
+     */
+    public Object clone() throws CloneNotSupportedException {
+        SHA1 that = (SHA1)super.clone();
+        that.pad = (byte[])this.pad.clone();
+        return that;
+    }
+ 
+    /**
+     * Returns the digest length in bytes.
+     *
+     * Can be used to allocate your own output buffer when
+     * computing multiple digests.
+     *
+     * Overrides the protected abstract method of
+     * <code>java.security.MessageDigestSpi</code>.
+     * @return the digest length in bytes.
+     */
+    public int engineGetDigestLength() {
+        return HASH_LENGTH;
+    }
+ 
+    /**
+     * Reset athen initialize the digest context.
+     *
+     * Overrides the protected abstract method of
+     * <code>java.security.MessageDigestSpi</code>.
+     */
+    protected void engineReset() {
+        int i = 60;
+        do {
+           pad[i    ] = (byte)0x00;
+           pad[i + 1] = (byte)0x00;
+           pad[i + 2] = (byte)0x00;
+           pad[i + 3] = (byte)0x00;
+        } while ((i -= 4) >= 0);
+        padding = 0;
+        bytes = 0;
+        init();
+    }
+ 
+    /**
+     * Initialize the digest context.
+     */
+    protected void init() {
+        hA = 0x67452301;
+        hB = 0xefcdab89;
+        hC = 0x98badcfe;
+        hD = 0x10325476;
+        hE = 0xc3d2e1f0;
+    }
+ 
+    /**
+     * Updates the digest using the specified byte.
+     * Requires internal buffering, and may be slow.
+     *
+     * Overrides the protected abstract method of
+     * java.security.MessageDigestSpi.
+     * @param input  the byte to use for the update.
+     */
+    public void engineUpdate(byte input) {
+        bytes++;
+        if (padding < 63) {
+            pad[padding++] = input;
+            return;
+        }
+        pad[63] = input;
+        computeBlock(pad, 0);
+        padding = 0;
+    }
+ 
+    /**
+     * Updates the digest using the specified array of bytes,
+     * starting at the specified offset.
+     *
+     * Input length can be any size. May require internal buffering,
+     * if input blocks are not multiple of 64 bytes.
+     *
+     * Overrides the protected abstract method of
+     * java.security.MessageDigestSpi.
+     * @param input  the array of bytes to use for the update.
+     * @param offset  the offset to start from in the array of bytes.
+     * @param len  the number of bytes to use, starting at offset.
+     */
+    public void engineUpdate(byte[] input, int offset, int len) {
+        if (offset >= 0 && len >= 0 && offset + len <= input.length) {
+            bytes += len;
+            /* Terminate the previous block. */
+            int padlen = 64 - padding;
+            if (padding > 0 && len >= padlen) {
+                System.arraycopy(input, offset, pad, padding, padlen);
+                computeBlock(pad, 0);
+                padding = 0;
+                offset += padlen;
+                len -= padlen;
+            }
+            /* Loop on large sets of complete blocks. */
+            while (len >= 512) {
+                computeBlock(input, offset);
+                computeBlock(input, offset + 64);
+                computeBlock(input, offset + 128);
+                computeBlock(input, offset + 192);
+                computeBlock(input, offset + 256);
+                computeBlock(input, offset + 320);
+                computeBlock(input, offset + 384);
+                computeBlock(input, offset + 448);
+                offset += 512;
+                len -= 512;
+            }
+            /* Loop on remaining complete blocks. */
+            while (len >= 64) {
+                computeBlock(input, offset);
+                offset += 64;
+                len -= 64;
+            }
+            /* remaining bytes kept for next block. */
+            if (len > 0) {
+                System.arraycopy(input, offset, pad, padding, len);
+                padding += len;
+            }
+            return;
+        }
+        throw new ArrayIndexOutOfBoundsException(offset);
+    }
+ 
+    /**
+     * Completes the hash computation by performing final operations
+     * such as padding. Computes the final hash and returns the final
+     * value as a byte[20] array. Once engineDigest has been called,
+     * the engine will be automatically reset as specified in the
+     * JavaSecurity MessageDigest specification.
+     *
+     * For faster operations with multiple digests, allocate your own
+     * array and use engineDigest(byte[], int offset, int len).
+     *
+     * Overrides the protected abstract method of
+     * java.security.MessageDigestSpi.
+     * @return the length of the digest stored in the output buffer.
+     */
+    public byte[] engineDigest() {
+        try {
+            final byte hashvalue[] = new byte[HASH_LENGTH];
+            engineDigest(hashvalue, 0, HASH_LENGTH);
+            return hashvalue;
+        } catch (DigestException e) {
+            return null;
+        }
+    }
+ 
+    /**
+     * Completes the hash computation by performing final operations
+     * such as padding. Once engineDigest has been called, the engine
+     * will be automatically reset (see engineReset).
+     *
+     * Overrides the protected abstract method of
+     * java.security.MessageDigestSpi.
+     * @param hashvalue  the output buffer in which to store the digest.
+     * @param offset  offset to start from in the output buffer
+     * @param len  number of bytes within buf allotted for the digest.
+     *             Both this default implementation and the SUN provider
+     *             do not return partial digests.  The presence of this
+     *             parameter is solely for consistency in our API's.
+     *             If the value of this parameter is less than the
+     *             actual digest length, the method will throw a
+     *             DigestException.  This parameter is ignored if its
+     *             value is greater than or equal to the actual digest
+     *             length.
+     * @return  the length of the digest stored in the output buffer.
+     */
+    public int engineDigest(byte[] hashvalue, int offset, final int len)
+            throws DigestException {
+        if (len >= HASH_LENGTH) {
+            if (hashvalue.length - offset >= HASH_LENGTH) {
+                /* Flush the trailing bytes, adding padding bytes into last
+                 * blocks. */
+                int i;
+                /* Add padding null bytes but replace the last 8 padding bytes
+                 * by the little-endian 64-bit digested message bit-length. */
+                pad[i = padding] = (byte)0x80; /* required 1st padding byte */
+                /* Check if 8 bytes available in pad to store the total
+                 * message size */
+                switch (i) { /* INVARIANT: i must be in [0..63] */
+                case 52: pad[53] = (byte)0x00; /* no break; falls thru */
+                case 53: pad[54] = (byte)0x00; /* no break; falls thru */
+                case 54: pad[55] = (byte)0x00; /* no break; falls thru */
+                case 55: break;
+                case 56: pad[57] = (byte)0x00; /* no break; falls thru */
+                case 57: pad[58] = (byte)0x00; /* no break; falls thru */
+                case 58: pad[59] = (byte)0x00; /* no break; falls thru */
+                case 59: pad[60] = (byte)0x00; /* no break; falls thru */
+                case 60: pad[61] = (byte)0x00; /* no break; falls thru */
+                case 61: pad[62] = (byte)0x00; /* no break; falls thru */
+                case 62: pad[63] = (byte)0x00; /* no break; falls thru */
+                case 63:
+                    computeBlock(pad, 0);
+                    /* Clear the 56 first bytes of pad[]. */
+                    i = 52;
+                    do {
+                        pad[i    ] = (byte)0x00;
+                        pad[i + 1] = (byte)0x00;
+                        pad[i + 2] = (byte)0x00;
+                        pad[i + 3] = (byte)0x00;
+                    } while ((i -= 4) >= 0);
+                    break;
+                default:
+                    /* Clear the rest of 56 first bytes of pad[]. */
+                    switch (i & 3) {
+                    case 3: i++;
+                            break;
+                    case 2: pad[(i += 2) - 1] = (byte)0x00;
+                            break;
+                    case 1: pad[(i += 3) - 2] = (byte)0x00;
+                            pad[ i       - 1] = (byte)0x00;
+                            break;
+                    case 0: pad[(i += 4) - 3] = (byte)0x00;
+                            pad[ i       - 2] = (byte)0x00;
+                            pad[ i       - 1] = (byte)0x00;
+                    }
+                    do {
+                        pad[i    ] = (byte)0x00;
+                        pad[i + 1] = (byte)0x00;
+                        pad[i + 2] = (byte)0x00;
+                        pad[i + 3] = (byte)0x00;
+                    } while ((i += 4) < 56);
+                }
+                /* Convert the message size from bytes to big-endian bits. */
+                pad[56] = (byte)((i = (int)(bytes >>> 29)) >> 24);
+                pad[57] = (byte)(i >>> 16);
+                pad[58] = (byte)(i >>> 8);
+                pad[59] = (byte)i;
+                pad[60] = (byte)((i = (int)bytes << 3) >> 24);
+                pad[61] = (byte)(i >>> 16);
+                pad[62] = (byte)(i >>> 8);
+                pad[63] = (byte)i;
+                computeBlock(pad, 0);
+                /* Return the computed digest in big-endian byte order. */
+                hashvalue[offset     ] = (byte)((i = hA) >>> 24);
+                hashvalue[offset +  1] = (byte)(i >>> 16);
+                hashvalue[offset +  2] = (byte)(i >>> 8);
+                hashvalue[offset +  3] = (byte)i;
+                hashvalue[offset +  4] = (byte)((i = hB) >>> 24);
+                hashvalue[offset += 5] = (byte)(i >>> 16);
+                hashvalue[offset +  1] = (byte)(i >>> 8);
+                hashvalue[offset +  2] = (byte)i;
+                hashvalue[offset +  3] = (byte)((i = hC) >>> 24);
+                hashvalue[offset +  4] = (byte)(i >>> 16);
+                hashvalue[offset += 5] = (byte)(i >>> 8);
+                hashvalue[offset +  1] = (byte)i;
+                hashvalue[offset +  2] = (byte)((i = hD) >>> 24);
+                hashvalue[offset +  3] = (byte)(i >>> 16);
+                hashvalue[offset +  4] = (byte)(i >>> 8);
+                hashvalue[offset += 5] = (byte)i;
+                hashvalue[offset +  1] = (byte)((i = hE) >>> 24);
+                hashvalue[offset +  2] = (byte)(i >>> 16);
+                hashvalue[offset +  3] = (byte)(i >>> 8);
+                hashvalue[offset +  4] = (byte)i;
+                engineReset(); /* clear the evidence */
+                return HASH_LENGTH;
+            }
+            throw new DigestException(
+                "insufficient space in output buffer to store the digest");
+        }
+        throw new DigestException("partial digests not returned");
+    }
+ 
+    /**
+     * Updates the digest using the specified array of bytes,
+     * starting at the specified offset, but an implied length
+     * of exactly 64 bytes.
+     *
+     * Requires no internal buffering, but assumes a fixed input size,
+     * in which the required padding bytes may have been added.
+     *
+     * @param input  the array of bytes to use for the update.
+     * @param offset  the offset to start from in the array of bytes.
+     */
+    private void computeBlock(final byte[] input, int offset) {
+        /* Local temporary work variables for intermediate digests. */
+        int a, b, c, d, e;
+        /* Cache the input block into the local working set of 32-bit
+         * values, in big-endian byte order. Be careful when
+         * widening bytes or integers due to sign extension! */
+        int i00, i01, i02, i03, i04, i05, i06, i07,
+            i08, i09, i10, i11, i12, i13, i14, i15;
+        /* Use hash schedule function Ch (rounds 0..19):
+         *   Ch(x,y,z) = (x & y) ^ (~x & z) = (x & (y ^ z)) ^ z,
+         * and K00 = .... = K19 = 0x5a827999. */
+        /* First pass, on big endian input (rounds 0..15). */
+        e =  hE
+          +  (((a = hA) << 5) | (a >>> 27)) + 0x5a827999 // K00
+          +  (((b = hB) & ((c = hC)      ^ (d = hD))) ^ d) // Ch(b,c,d)
+          +  (i00 =  input[offset     ] << 24
+                  | (input[offset +  1] & 0xff) << 16
+                  | (input[offset +  2] & 0xff) << 8
+                  | (input[offset +  3] & 0xff)); // W00
+        d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K01
+          +  ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+          +  (i01 =  input[offset +  4] << 24
+                  | (input[offset += 5] & 0xff) << 16
+                  | (input[offset +  1] & 0xff) << 8
+                  | (input[offset +  2] & 0xff)); // W01
+        c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K02
+          +  ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+          +  (i02 =  input[offset +  3] << 24
+                  | (input[offset +  4] & 0xff) << 16
+                  | (input[offset += 5] & 0xff) << 8
+                  | (input[offset +  1] & 0xff)); // W02
+        b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K03
+          +  ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+          +  (i03 =  input[offset +  2] << 24
+                  | (input[offset +  3] & 0xff) << 16
+                  | (input[offset +  4] & 0xff) << 8
+                  | (input[offset += 5] & 0xff)); // W03
+        a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K04
+          +  ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+          +  (i04 =  input[offset +  1] << 24
+                  | (input[offset +  2] & 0xff) << 16
+                  | (input[offset +  3] & 0xff) << 8
+                  | (input[offset +  4] & 0xff)); // W04
+        e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K05
+          +  ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
+          +  (i05 =  input[offset += 5] << 24
+                  | (input[offset +  1] & 0xff) << 16
+                  | (input[offset +  2] & 0xff) << 8
+                  | (input[offset +  3] & 0xff)); // W05
+        d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K06
+          +  ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+          +  (i06 =  input[offset +  4] << 24
+                  | (input[offset += 5] & 0xff) << 16
+                  | (input[offset +  1] & 0xff) << 8
+                  | (input[offset +  2] & 0xff)); // W06
+        c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K07
+          +  ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+          +  (i07 =  input[offset +  3] << 24
+                  | (input[offset +  4] & 0xff) << 16
+                  | (input[offset += 5] & 0xff) << 8
+                  | (input[offset +  1] & 0xff)); // W07
+        b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K08
+          +  ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+          +  (i08 =  input[offset +  2] << 24
+                  | (input[offset +  3] & 0xff) << 16
+                  | (input[offset +  4] & 0xff) << 8
+                  | (input[offset += 5] & 0xff)); // W08
+        a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K09
+          +  ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+          +  (i09 =  input[offset +  1] << 24
+                  | (input[offset +  2] & 0xff) << 16
+                  | (input[offset +  3] & 0xff) << 8
+                  | (input[offset +  4] & 0xff)); // W09
+        e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K10
+          +  ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
+          +  (i10 =  input[offset += 5] << 24
+                  | (input[offset +  1] & 0xff) << 16
+                  | (input[offset +  2] & 0xff) << 8
+                  | (input[offset +  3] & 0xff)); // W10
+        d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K11
+          +  ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+          +  (i11 =  input[offset +  4] << 24
+                  | (input[offset += 5] & 0xff) << 16
+                  | (input[offset +  1] & 0xff) << 8
+                  | (input[offset +  2] & 0xff)); // W11
+        c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K12
+          +  ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+          +  (i12 =  input[offset +  3] << 24
+                  | (input[offset +  4] & 0xff) << 16
+                  | (input[offset += 5] & 0xff) << 8
+                  | (input[offset +  1] & 0xff)); // W12
+        b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K13
+          +  ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+          +  (i13 =  input[offset +  2] << 24
+                  | (input[offset +  3] & 0xff) << 16
+                  | (input[offset +  4] & 0xff) << 8
+                  | (input[offset += 5] & 0xff)); // W13
+        a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K14
+          +  ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+          +  (i14 =  input[offset +  1] << 24
+                  | (input[offset +  2] & 0xff) << 16
+                  | (input[offset +  3] & 0xff) << 8
+                  | (input[offset +  4] & 0xff)); // W14
+        e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K15
+          +  ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
+          +  (i15 =  input[offset += 5] << 24
+                  | (input[offset +  1] & 0xff) << 16
+                  | (input[offset +  2] & 0xff) << 8
+                  | (input[offset +  3] & 0xff)); // W15
+        /* Second pass, on scheduled input (rounds 16..31). */
+        d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K16
+          +  ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+          +  (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W16
+        c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K17
+          +  ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+          +  (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W17
+        b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K18
+          +  ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+          +  (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W18
+        a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K19
+          +  ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+          +  (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W19
+        /* Use hash schedule function Parity (rounds 20..39):
+         *   Parity(x,y,z) = x ^ y ^ z,
+         * and K20 = .... = K39 = 0x6ed9eba1. */
+        e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K20
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W20
+        d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K21
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W21
+        c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K22
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W22
+        b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K23
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W23
+        a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K24
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W24
+        e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K25
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W25
+        d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K26
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W26
+        c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K27
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W27
+        b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K28
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W28
+        a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K29
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W29
+        e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K30
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W30
+        d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K31
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W31
+        /* Third pass, on scheduled input (rounds 32..47). */
+        c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K32
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W32
+        b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K33
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W33
+        a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K34
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W34
+        e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K35
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W35
+        d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K36
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W36
+        c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K37
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W37
+        b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K38
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W38
+        a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K39
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W39
+        /* Use hash schedule function Maj (rounds 40..59):
+         *   Maj(x,y,z) = (x&y) ^ (x&z) ^ (y&z) = (x & y) | ((x | y) & z),
+         * and K40 = .... = K59 = 0x8f1bbcdc. */
+        e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K40
+          +  ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+          +  (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W40
+        d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K41
+          +  ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+          +  (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W41
+        c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K42
+          +  ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+          +  (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W42
+        b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K43
+          +  ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+          +  (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W43
+        a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K44
+          +  ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+          +  (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W44
+        e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K45
+          +  ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+          +  (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W45
+        d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K46
+          +  ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+          +  (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W46
+        c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K47
+          +  ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+          +  (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W47
+        /* Fourth pass, on scheduled input (rounds 48..63). */
+        b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K48
+          +  ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+          +  (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W48
+        a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K49
+          +  ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+          +  (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W49
+        e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K50
+          +  ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+          +  (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W50
+        d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K51
+          +  ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+          +  (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W51
+        c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K52
+          +  ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+          +  (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W52
+        b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K53
+          +  ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+          +  (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W53
+        a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K54
+          +  ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+          +  (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W54
+        e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K55
+          +  ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+          +  (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W55
+        d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K56
+          +  ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+          +  (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W56
+        c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K57
+          +  ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+          +  (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W57
+        b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K58
+          +  ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+          +  (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W58
+        a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K59
+          +  ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+          +  (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W59
+        /* Use hash schedule function Parity (rounds 60..79):
+         *   Parity(x,y,z) = x ^ y ^ z,
+         * and K60 = .... = K79 = 0xca62c1d6. */
+        e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K60
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W60
+        d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K61
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W61
+        c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K62
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W62
+        b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K63
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W63
+        /* Fifth pass, on scheduled input (rounds 64..79). */
+        a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K64
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W64
+        e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K65
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W65
+        d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K66
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W66
+        c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K67
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W67
+        b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K68
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W68
+        a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K69
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W69
+        e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K70
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W70
+        d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K71
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W71
+        c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K72
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W72
+        b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K73
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W73
+        a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K74
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W74
+        e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K75
+          +  (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+          +  (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W75
+        d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K76
+          +  (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+          +  (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W76
+        c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K77
+          +  (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+          +  (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W77
+        /* Terminate the last two rounds of fifth pass,
+         * feeding the final digest on the fly. */
+        hB +=
+        b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K78
+          +  (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+          +  (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W78
+        hA +=
+        a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K79
+          +  (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+          +  (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W79
+        hE += e;
+        hD += d;
+        hC += /* c= */ (c << 30) | (c >>> 2);
+    }
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/haver/SHA1.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java      2005-05-23 
20:58:15 UTC (rev 712)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java      2005-05-23 
21:06:31 UTC (rev 713)
@@ -0,0 +1,255 @@
+/*
+ * CMod.java
+ *
+ * Created on May 22, 2005, 3:37 PM
+ */
+
+package org.haverdev.javer2;
+import org.haverdev.haver.*;
+import java.util.*;
+import java.text.*;
+import java.io.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class CMod extends Callback {    
+    Client c = new Client();
+    Timer t = new Timer(true);
+    String channel;
+    
+    protected void ping() {
+        logMessage("ping!");
+        TimerTask ping = new TimerTask() {
+            public void run() {
+                ping();
+            }
+        };
+        t.schedule(ping, 10000);
+    }
+    
+    /** Creates a new instance of CMod */
+    public CMod(String host, int port, String name, String channel) throws 
Throwable {
+        this.channel = channel;
+        c.addNotify(this);
+        c.syncConnect(host, port, name);
+        ping();
+        logMessage("connected");
+    }
+    
+    protected static void logMessage(String s) {
+        Date now = new Date(System.currentTimeMillis());
+        DateFormat tf = DateFormat.getTimeInstance(DateFormat.MEDIUM);
+        String time = tf.format(now);
+        System.err.println("[" + time + "] " + s);
+    }
+    
+    public static void main(String[] args) throws Throwable {
+        new CMod("localhost", 13456, "cmod", "lobby");
+    }
+    
+    static final int STATE_IDLE = 0;
+    static final int STATE_JOIN = 1;
+    static final int STATE_CHAT = 2;
+    static final int STATE_VOTE = 3;
+    
+    static final int joinTimeout = 30;
+    static final int chatTimeout = 3; // XXX
+    
+    int current_state = 0;
+    
+    HashSet players = null;
+    HashMap votes   = null;
+    TimerTask startTask = null;
+    TimerTask voteTask  = null;
+    
+    public synchronized void reset() {
+        current_state = 0;
+        players = null;
+        votes = null;
+        if (startTask != null)
+            startTask.cancel();
+        startTask = null;
+        if (voteTask != null)
+            voteTask.cancel();
+        voteTask = null;
+    }
+    
+    public void onDisconnected(Client source, java.io.IOException e) throws 
java.io.IOException {
+        System.exit(0);
+    }
+
+    public synchronized void onPrivateMessage(Client source, String from, 
String type, String[] args) throws java.io.IOException {
+        if (current_state != STATE_VOTE) return;
+        boolean vote;
+        if (args[0].equals("yes"))
+            vote = true;
+        else if (args[0].equals("no"))
+            vote = false;
+        else return;
+        
+        if (votes.containsKey(from)) {
+            c.sendPrivateMessage(from, "say", "You have already voted.");
+            return;
+        }
+        
+        votes.put(from, new Boolean(vote));
+        c.sendPrivateMessage(from, "say", "Your vote has been recorded.");
+        tallyVotes();
+    }
+
+    public synchronized void onPublicMessage(Client source, String channel, 
String from, String type, String[] args) throws java.io.IOException {
+        
+        if (!channel.equals(this.channel)) return;
+        if (!type.equals("say")) return;
+        
+        if (current_state != STATE_IDLE && args[0].equals("!reset")) reset();
+        
+        switch (current_state) {
+            case STATE_IDLE:
+                idleMsg(from, args[0]);
+                break;
+            case STATE_JOIN:
+                joinMsg(from, args[0]);
+                break;
+            default:
+                return;
+        }
+    }
+    
+    public void idleMsg(String from, String msg) throws IOException {
+        if (!msg.equals("!start")) return;
+        current_state = STATE_JOIN;
+        startTask = new TimerTask() {
+            public void run() {
+                try {
+                    startGame();
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+        };
+        t.schedule(startTask, joinTimeout * 1000);
+        c.sendPublicMessage(channel, "say",
+                "A game of conspiracy has begun (or has it?). You have " +
+                joinTimeout + " seconds to type '!join'");
+        players = new HashSet();
+    }
+    
+    public void joinMsg(String from, String msg) throws IOException {
+        if (!msg.equals("!join")) return;
+        if (players.contains(from)) {
+            c.sendPrivateMessage(from, "say", "You have already joined.");
+            return;
+        }
+        players.add(from);
+        c.sendPublicMessage(channel, "say", from + " has been added to the 
list of players.");
+    }
+    
+    String innocent = null;
+    
+    public synchronized void startGame() throws IOException {
+        if (current_state != STATE_JOIN) return;
+        if (players.size() < 3) {
+            c.sendPublicMessage(channel, "say", "Not enough players, 
aborting.");
+            reset();
+            return;
+        }
+        current_state = STATE_CHAT;
+        Random r = new Random();
+        if (r.nextBoolean()) {
+            Object[] players_a = players.toArray();
+            innocent = (String)players_a[r.nextInt(players_a.length)];
+        }
+        Iterator i = players.iterator();
+        while (i.hasNext()) {
+            String current = (String)i.next();
+            if (innocent == null || innocent.equals(current)) {
+                c.sendPrivateMessage(current, "say", "You are an innocent in 
this game.");
+            } else {
+                c.sendPrivateMessage(current, "say", "You are a conspirator, 
and the innocent is " + innocent);
+            }
+        }
+        c.sendPublicMessage(channel, "say", "The game is afoot! You have " + 
chatTimeout + " seconds to point fingers before I'll come back demanding 
answers from you lot.");
+        voteTask = new TimerTask() {
+            public void run() {
+                try { startVote(); }
+                catch (Throwable t) { t.printStackTrace(); }
+            }
+        };
+        t.schedule(voteTask, 1000 * chatTimeout);
+    }
+    
+    public synchronized void startVote() throws IOException {
+        if (current_state != STATE_CHAT) return;
+        c.sendPublicMessage(channel, "say", "Time's up, so stop these baseless 
public accusations, and begin private accusations. To me.");
+        c.sendPublicMessage(channel, "say", "Message me with 'yes' if you 
think there's a conspiracy, or 'no' if you think it's all a bunch of baseless 
liberal propaganda.");
+        current_state = STATE_VOTE;
+        votes = new HashMap();
+    }
+    
+    public synchronized void tallyVotes() throws IOException {
+        if (current_state != STATE_VOTE) return;
+        if (votes.size() < players.size()) return;
+        HashMap wins = new HashMap();
+        if (innocent != null) {
+            boolean innocent_vote = 
((Boolean)votes.get(innocent)).booleanValue();
+            Iterator i = players.iterator();
+            while (i.hasNext()) {
+                String player = (String)i.next();
+                boolean win;
+                if (player.equals(innocent))
+                    win = innocent_vote;
+                else
+                    win = !innocent_vote;
+                wins.put(player, new Boolean(win));
+            }
+        } else {
+            Iterator i = players.iterator();
+            while (i.hasNext()) {
+                String player = (String)i.next();
+                boolean vote = ((Boolean)votes.get(player)).booleanValue();
+                wins.put(player, new Boolean(!vote));
+            }
+        }
+        
+        String namehead = "NAME        ";
+        String votehead = "VOTE";
+        String winshead = "WIN?";
+        
+        c.sendPublicMessage(channel, "say", namehead + " " + votehead + " " + 
winshead + " ");
+        Iterator i = players.iterator();
+        while (i.hasNext()) {
+            String player = (String)i.next();
+            Boolean vote_o = (Boolean)votes.get(player);
+            Boolean win_o = (Boolean)wins.get(player);
+            boolean vote_b = vote_o.booleanValue();
+            boolean win_b = win_o.booleanValue();
+            String vote_s = vote_b ? "YES  " : "NO   ";
+            String win_s = win_b ? "YES " : "NO  ";
+            
+            StringBuffer buf = new StringBuffer(namehead.length() + 11);
+            
+            int length = player.length();
+            if (length > namehead.length())
+                length = namehead.length();
+            String snippedname = player.substring(0, length);
+            int pad = namehead.length() - length + 1;
+            buf.append(snippedname);
+            while (pad-- > 0)
+                buf.append(' ');
+            buf.append(vote_s);
+            buf.append(win_s);
+            c.sendPublicMessage(channel, "say", buf.toString());
+        }
+        
+        c.sendPublicMessage(channel, "say", "Game Over. Thanks for playing!");
+        reset();
+    }
+
+    public void onAccept(Client source, String name) throws IOException, 
InterruptedException {
+        c.syncJoin(channel);
+        logMessage("joined");
+    }
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/javer2/CMod.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/clients/Javer2/src/org/haverdev/javer2/Main.java
===================================================================
--- trunk/clients/Javer2/src/org/haverdev/javer2/Main.java      2005-05-23 
20:58:15 UTC (rev 712)
+++ trunk/clients/Javer2/src/org/haverdev/javer2/Main.java      2005-05-23 
21:06:31 UTC (rev 713)
@@ -0,0 +1,68 @@
+/*
+ * Main.java
+ *
+ * Created on May 21, 2005, 8:46 PM
+ */
+
+package org.haverdev.javer2;
+import org.haverdev.haver.*;
+
+/**
+ *
+ * @author bdonlan
+ */
+public class Main extends org.haverdev.haver.Callback {
+    Client c;
+    
+    /** Creates a new instance of Main */
+    public Main() throws Throwable {
+        c = new Client();
+        c.addNotify(this);
+        c.syncConnect("localhost", 15455, "bd_");
+        System.out.println("out");
+    }
+    
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) throws Throwable {
+        // TODO code application logic here
+        new Main();
+    }
+
+    public String join(String joinBy, String[] parts) {
+        if (parts.length == 0) return "";
+        
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < parts.length; i++) {
+            b.append(joinBy);
+            b.append(parts[i]);
+        }
+        return b.toString().substring(joinBy.length());
+    }
+    
+    public void onDisconnected(Client source, java.io.IOException e) throws 
java.io.IOException {
+        System.out.println("dc");
+    }
+
+    public void onConnect(Client source) throws java.io.IOException {
+        System.out.println("connected");
+    }
+
+    public void onOutgoingLine(Client source, String[] args) throws 
java.io.IOException {
+        //System.out.println("out:");
+    }
+
+    public void onIncomingLine(Client source, String[] args) throws 
java.io.IOException {
+        //System.out.println("in");
+    }
+    
+    public void onAccept(Client source, String nick) throws 
java.io.IOException {
+        System.out.println("Accepted: " + nick);
+        try {
+            String[] channels = source.syncList("&lobby", "channel");
+            System.out.println("channels: " + join(", ", channels));
+        } catch (Throwable t) { t.printStackTrace(); }
+    }
+    
+}


Property changes on: trunk/clients/Javer2/src/org/haverdev/javer2/Main.java
___________________________________________________________________
Name: svn:eol-style
   + native


Reply via email to