You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by hu...@apache.org on 2014/01/03 11:17:57 UTC

[24/50] [abbrv] CLOUDSTACK-5344: Updated to allow rdp console to access hyper-v vm virtual framebuffer.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java
deleted file mode 100644
index 7dd70eb..0000000
--- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java
+++ /dev/null
@@ -1,237 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-package rdpclient;
-
-import streamer.ByteBuffer;
-import streamer.Element;
-import streamer.Link;
-import streamer.MockSink;
-import streamer.MockSource;
-import streamer.OneTimeSwitch;
-import streamer.Pipeline;
-import streamer.PipelineImpl;
-
-/**
- * Once the External Security Protocol handshake has run to completion, the
- * client MUST continue with the connection sequence by sending the MCS Connect
- * Initial PDU to the server over the newly established secure channel.
- *
- *
- * @see http://msdn.microsoft.com/en-us/library/cc240663.aspx
- */
-public class ServerX224ConnectionConfirmPDU extends OneTimeSwitch {
-
-    public static final int X224_TPDU_CONNECTION_REQUEST = 0xe0;
-    public static final int X224_TPDU_CONNECTION_CONFIRM = 0xd0;
-    public static final int X224_TPDU_DISCONNECTION_REQUEST = 0x80;
-    public static final int X224_TPDU_DISCONNECTION_CONFIRM = 0xc0;
-    public static final int X224_TPDU_EXPEDITED_DATA = 0x10;
-    public static final int X224_TPDU_DATA_ACKNOWLEDGE = 0x61;
-    public static final int X224_TPDU_EXPEDITET_ACKNOWLEDGE = 0x40;
-    public static final int X224_TPDU_REJECT = 0x51;
-    public static final int X224_TPDU_ERROR = 0x70;
-    public static final int X224_TPDU_PROTOCOL_IDENTIFIER = 0x01;
-
-    /**
-     * The server requires that the client support Enhanced RDP Security with
-     * either TLS 1.0, 1.1 or 1.2 or CredSSP. If only CredSSP was requested then
-     * the server only supports TLS.
-     */
-    public static final int SSL_REQUIRED_BY_SERVER = 0x00000001;
-
-    /**
-     * The server is configured to only use Standard RDP Security mechanisms and
-     * does not support any External Security Protocols.
-     */
-    public static final int SSL_NOT_ALLOWED_BY_SERVER = 0x00000002;
-
-    /**
-     * The server does not possess a valid authentication certificate and cannot
-     * initialize the External Security Protocol Provider.
-     */
-    public static final int SSL_CERT_NOT_ON_SERVER = 0x00000003;
-
-    /**
-     * The list of requested security protocols is not consistent with the current
-     * security protocol in effect. This error is only possible when the Direct
-     * Approach is used and an External Security Protocolis already being used.
-     */
-    public static final int INCONSISTENT_FLAGS = 0x00000004;
-
-    /**
-     * The server requires that the client support Enhanced RDP Security with
-     * CredSSP.
-     */
-    public static final int HYBRID_REQUIRED_BY_SERVER = 0x00000005;
-
-    /**
-     * The server requires that the client support Enhanced RDP Security with TLS
-     * 1.0, 1.1 or 1.2 and certificate-based client authentication.
-     */
-    public static final int SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 0x00000006;
-
-    public ServerX224ConnectionConfirmPDU(String id) {
-        super(id);
-    }
-
-    @Override
-    protected void handleOneTimeData(ByteBuffer buf, Link link) {
-        if (buf == null)
-            return;
-
-        if (verbose)
-            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
-
-        int x224Length = buf.readVariableSignedIntLE();
-
-        int x224Type = buf.readUnsignedByte();
-        if (x224Type != X224_TPDU_CONNECTION_CONFIRM)
-            throw new RuntimeException("Unexpected type of packet. Expected type: " + X224_TPDU_CONNECTION_CONFIRM + " (CONNECTION CONFIRM), actual type: " + x224Type +
-                ", length: " + x224Length + ", buf: " + buf + ".");
-
-        // Ignore destination reference, because client side has only one node
-        buf.skipBytes(2);
-
-        // Source reference
-        // int srcRef = buf.readUnsignedShort();
-        buf.skipBytes(2);
-
-        // Ignore class and options
-        buf.skipBytes(1);
-
-        // RDP_NEG_RSP::type (TYPE_RDP_NEG_RSP)
-        int negType = buf.readUnsignedByte();
-
-        // RDP_NEG_RSP::flags (0)
-        buf.skipBytes(1); // Ignore: always 0
-
-        // RDP_NEG_RSP::length (always 8 bytes)
-        int length = buf.readUnsignedShortLE();
-
-        if (length != 8)
-            throw new RuntimeException("Unexpected length of buffer. Expected value: 8, actual value: " + length + ", RDP NEG buf: " + buf + ".");
-
-        // RDP_NEG_RSP: Selected protocols (PROTOCOL_SSL)
-        int protocol = buf.readSignedIntLE();
-
-        if (negType != RdpConstants.RDP_NEG_REQ_TYPE_NEG_RSP) {
-            // Parse error code, see
-            // http://msdn.microsoft.com/en-us/library/cc240507.aspx
-            int errorCode = protocol;
-            String message = "Unknown error.";
-            switch (errorCode) {
-                case SSL_REQUIRED_BY_SERVER:
-                    message =
-                        "The server requires that the client support Enhanced RDP Security with either TLS 1.0, 1.1 or 1.2 or CredSSP. If only CredSSP was requested then the server only supports TLS.";
-                    break;
-
-                case SSL_NOT_ALLOWED_BY_SERVER:
-                    message = "The server is configured to only use Standard RDP Security mechanisms and does not support any External Security Protocols.";
-                    break;
-
-                case SSL_CERT_NOT_ON_SERVER:
-                    message = "The server does not possess a valid authentication certificate and cannot initialize the External Security Protocol Provider.";
-                    break;
-
-                case INCONSISTENT_FLAGS:
-                    message =
-                        "The list of requested security protocols is not consistent with the current security protocol in effect. This error is only possible when the Direct Approach is used and an External Security Protocolis already being used.";
-                    break;
-
-                case HYBRID_REQUIRED_BY_SERVER:
-                    message = "The server requires that the client support Enhanced RDP Security with CredSSP.";
-                    break;
-
-                case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER:
-                    message = "The server requires that the client support Enhanced RDP Security  with TLS 1.0, 1.1 or 1.2 and certificate-based client authentication.";
-                    break;
-
-            }
-            throw new RuntimeException("Connection failure: " + message);
-        }
-
-        if (protocol != RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL)
-            throw new RuntimeException("Unexpected protocol type. Expected protocol type: " + RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL + " (SSL), actual response type: " +
-                protocol + ", RDP NEG buf: " + buf + ".");
-
-        if (verbose)
-            System.out.println("[" + this + "] INFO: RDP Negotiation response. Type: " + negType + ", protocol: " + protocol + ".");
-
-        // Next: upgrade socket to SSL, send ConnectInitial packet
-        switchOff();
-    }
-
-    /**
-     * Example.
-     *
-     */
-    public static void main(String args[]) {
-        // System.setProperty("streamer.Link.debug", "true");
-        System.setProperty("streamer.Element.debug", "true");
-        // System.setProperty("streamer.Pipeline.debug", "true");
-
-//    byte[] packet = new byte[] {
-//
-//        0x03, // -> TPKT Header: TPKT version = 3
-//        0x00, // TPKT Header: Reserved = 0
-//        0x00, 0x13, // TPKT Header: Packet length - (total = 19 bytes)
-//        0x0e, // X.224: Length indicator (14 bytes)
-//        (byte) 0xd0, // X.224: Type (high nibble) = 0xd = CC TPDU; credit
-//                     // (low nibble) = 0
-//        0x00, 0x00, // X.224: Destination reference = 0
-//        0x12, 0x34, // X.224: Source reference = 0x1234 (bogus value)
-//        0x00, // X.224: Class and options = 0
-//
-//        0x02, // RDP_NEG_RSP::type (TYPE_RDP_NEG_RSP)
-//        0x00, // RDP_NEG_RSP::flags (0)
-//        0x08, 0x00, // RDP_NEG_RSP::length (8 bytes)
-//        0x01, 0x00, 0x00, 0x00 // RDP_NEG_RSP: Selected protocols (PROTOCOL_SSL)
-//    };
-
-        // Connection failure
-        // 03 00 00 13 0e d0 00 00 12 34 00 03 00 08 00 05 00 00 00
-        byte[] packet = new byte[] {
-
-            0x03, // -> TPKT Header: TPKT version = 3
-            0x00, // TPKT Header: Reserved = 0
-            0x00, 0x13, // TPKT Header: Packet length - (total = 19 bytes)
-            0x0e, // X.224: Length indicator (14 bytes)
-            (byte)0xd0, // X.224: Type (high nibble) = 0xd = CC TPDU; credit
-                        // (low nibble) = 0
-            0x00, 0x00, // X.224: Destination reference = 0
-            0x12, 0x34, // X.224: Source reference = 0x1234 (bogus value)
-            0x00, // X.224: Class and options = 0
-            (byte)0x03, // Failure
-            (byte)0x00, // RDP_NEG_RSP::flags (0)
-            (byte)0x08, (byte)0x00, // RDP_NEG_RSP::length (8 bytes)
-            (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, // Code:  HYBRID_REQUIRED_BY_SERVER
-
-        };
-
-        MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
-        Element cc = new ServerX224ConnectionConfirmPDU("cc");
-        Element tpkt = new ServerTpkt("tpkt");
-        Element sink = new MockSink("sink", new ByteBuffer[] {});
-        Element mainSink = new MockSink("mainSink", new ByteBuffer[] {});
-
-        Pipeline pipeline = new PipelineImpl("test");
-        pipeline.add(source, tpkt, cc, sink, mainSink);
-        pipeline.link("source", "tpkt", "cc", "mainSink");
-        pipeline.link("cc >" + OTOUT, "sink");
-        pipeline.runMainLoop("source", STDOUT, false, false);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java
deleted file mode 100644
index 62b0f55..0000000
--- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-package rdpclient;
-
-import streamer.BaseElement;
-import streamer.ByteBuffer;
-import streamer.Link;
-
-public class ServerX224DataPdu extends BaseElement {
-
-    public static final int X224_TPDU_LAST_DATA_UNIT = 0x80;
-    public static final int X224_TPDU_DATA = 0xF0;
-
-    public ServerX224DataPdu(String id) {
-        super(id);
-    }
-
-    @Override
-    public void handleData(ByteBuffer buf, Link link) {
-        if (buf == null)
-            return;
-
-        if (verbose)
-            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
-
-        int headerLength = buf.readVariableSignedIntLE();
-
-        if (headerLength != 2)
-            throw new RuntimeException("Unexpected X224 Data PDU header length. Expected header length: 2 , actual header length: " + headerLength + ".");
-
-        // Read X224 type and options
-        int type = buf.readUnsignedByte(); // High nibble: type, low nibble:
-
-        if ((type & 0xf0) != X224_TPDU_DATA)
-            throw new RuntimeException("[" + this + "] ERROR: Unexepcted X224 packet type. Expected packet type: " + X224_TPDU_DATA +
-                " (X224_TPDU_DATA), actual packet type: " + type + ", buf: " + buf + ".");
-
-        int options = buf.readUnsignedByte();
-
-        if ((options & X224_TPDU_LAST_DATA_UNIT) != X224_TPDU_LAST_DATA_UNIT)
-            throw new RuntimeException("Unexepcted X224 packet options. Expected options: " + X224_TPDU_LAST_DATA_UNIT +
-                " (X224_TPDU_LAST_DATA_UNIT), actual packet options: " + options + ", buf: " + buf + ".");
-
-        ByteBuffer payload = buf.readBytes(buf.length - buf.cursor);
-
-        buf.unref();
-
-        pushDataToAllOuts(payload);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java
deleted file mode 100644
index 088094a..0000000
--- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-package rdpclient;
-
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.X509TrustManager;
-
-public class TrustAllX509TrustManager implements X509TrustManager {
-    @Override
-    public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
-        // TODO: ask user to confirm self-signed certificates
-    }
-
-    @Override
-    public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
-        // TODO: ask user to confirm self-signed certificates
-    }
-
-    @Override
-    public X509Certificate[] getAcceptedIssuers() {
-        // TODO: use system CA certificates here
-        return null;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java
deleted file mode 100644
index 7267708..0000000
--- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-package rdpclient;
-
-import streamer.ByteBuffer;
-import streamer.Direction;
-import streamer.Event;
-import streamer.Link;
-import streamer.OneTimeSwitch;
-
-public class UpgradeSocketToSSL extends OneTimeSwitch {
-
-    public UpgradeSocketToSSL(String id) {
-        super(id);
-    }
-
-    @Override
-    protected void onStart() {
-
-        sendEventToAllPads(Event.SOCKET_UPGRADE_TO_SSL, Direction.IN);
-        switchOff();
-    }
-
-    @Override
-    protected void handleOneTimeData(ByteBuffer buf, Link link) {
-        throw new RuntimeException("Unexpected data: " + buf + ".");
-
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java
new file mode 100755
index 0000000..f2b19e1
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java
@@ -0,0 +1,350 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.adapter;
+
+import java.awt.event.KeyEvent;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+import common.KeyOrder;
+
+public class AwtRdpKeyboardAdapter extends BaseElement {
+
+    /**
+     * Absence of this flag indicates a key-down event, while its presence
+     * indicates a key-release event.
+     */
+    public static final int FASTPATH_INPUT_KBDFLAGS_RELEASE = 0x01;
+
+    /**
+     * Keystroke message contains an extended scancode. For enhanced 101-key and
+     * 102-key keyboards, extended keys include the right ALT and right CTRL keys
+     * on the main section of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE
+     * DOWN and ARROW keys in the clusters to the left of the numeric keypad; and
+     * the Divide ("/") and ENTER keys in the numeric keypad.
+     */
+    public static final int FASTPATH_INPUT_KBDFLAGS_EXTENDED = 0x02;
+
+    public static final int FASTPATH_INPUT_EVENT_SCANCODE = 0;
+
+    public AwtRdpKeyboardAdapter(String id) {
+        super(id);
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        KeyOrder order = (KeyOrder)buf.getOrder();
+        buf.unref();
+
+        ByteBuffer outBuf = new ByteBuffer(2, true);
+
+        int scanCode = map_en_us(order.event);
+
+        // eventHeader (1 byte): An 8-bit, unsigned integer. The format of this
+        // field is the same as the eventHeader byte field described in section
+        // 2.2.8.1.2.2. The eventCode bitfield (3 bits in size) MUST be set to
+        // FASTPATH_INPUT_EVENT_SCANCODE (0). The eventFlags bitfield (5 bits in
+        // size) contains flags describing the keyboard event.
+        outBuf.writeByte((scanCode >> 8) | (FASTPATH_INPUT_EVENT_SCANCODE << 5) | ((order.pressed) ? 0 : FASTPATH_INPUT_KBDFLAGS_RELEASE));
+
+        // keyCode (1 byte): An 8-bit, unsigned integer. The scancode of the key
+        // which triggered the event.
+        outBuf.writeByte(scanCode);
+
+        // Push buffer to one pad only, so it can be modified without copying of
+        // data
+        pushDataToPad(STDOUT, outBuf);
+    }
+
+    /**
+     * Return key scan code (in lower byte) and extended flags (in second byte).
+     */
+    private int map_en_us(KeyEvent event) {
+        // Also set extended key flag when necessary.
+        // For enhanced 101-key and 102-key keyboards, extended keys include the
+        // right ALT and right CTRL keys on the main section of the keyboard; the
+        // INS, DEL, HOME, END, PAGE UP, PAGE DOWN and ARROW keys in the clusters to
+        // the left of the numeric keypad; and the Divide ("/") and ENTER keys in
+        // the numeric keypad.
+
+        switch (event.getKeyCode()) {
+        // Functional keys
+        case KeyEvent.VK_ESCAPE:
+            return 1;
+        case KeyEvent.VK_F1:
+            return 59;
+        case KeyEvent.VK_F2:
+            return 60;
+        case KeyEvent.VK_F3:
+            return 61;
+        case KeyEvent.VK_F4:
+            return 62;
+        case KeyEvent.VK_F5:
+            return 63;
+        case KeyEvent.VK_F6:
+            return 64;
+        case KeyEvent.VK_F7:
+            return 65;
+        case KeyEvent.VK_F8:
+            return 66;
+        case KeyEvent.VK_F9:
+            return 67;
+        case KeyEvent.VK_F10:
+            return 68;
+        case KeyEvent.VK_F11:
+            return 87;
+        case KeyEvent.VK_F12:
+            return 88;
+
+            // Row #1
+        case KeyEvent.VK_BACK_QUOTE:
+            return 41;
+        case KeyEvent.VK_1:
+            return 2;
+        case KeyEvent.VK_2:
+            return 3;
+        case KeyEvent.VK_3:
+            return 4;
+        case KeyEvent.VK_4:
+            return 5;
+        case KeyEvent.VK_5:
+            return 6;
+        case KeyEvent.VK_6:
+            return 7;
+        case KeyEvent.VK_7:
+            return 8;
+        case KeyEvent.VK_8:
+            return 9;
+        case KeyEvent.VK_9:
+            return 10;
+        case KeyEvent.VK_0:
+            return 11;
+        case KeyEvent.VK_MINUS:
+            return 12;
+        case KeyEvent.VK_EQUALS:
+            return 13;
+        case KeyEvent.VK_BACK_SPACE:
+            return 14;
+
+            // Row #2
+        case KeyEvent.VK_TAB:
+            return 15;
+        case KeyEvent.VK_Q:
+            return 16;
+        case KeyEvent.VK_W:
+            return 17;
+        case KeyEvent.VK_E:
+            return 18;
+        case KeyEvent.VK_R:
+            return 19;
+        case KeyEvent.VK_T:
+            return 20;
+        case KeyEvent.VK_Y:
+            return 21;
+        case KeyEvent.VK_U:
+            return 22;
+        case KeyEvent.VK_I:
+            return 23;
+        case KeyEvent.VK_O:
+            return 24;
+        case KeyEvent.VK_P:
+            return 25;
+        case KeyEvent.VK_OPEN_BRACKET:
+            return 26;
+        case KeyEvent.VK_CLOSE_BRACKET:
+            return 27;
+        case KeyEvent.VK_ENTER:
+            switch (event.getKeyLocation()) {
+            default:
+            case KeyEvent.KEY_LOCATION_STANDARD:
+                return 28;
+            case KeyEvent.KEY_LOCATION_NUMPAD:
+                return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 28;
+            }
+
+            // Row #3
+        case KeyEvent.VK_CAPS_LOCK:
+            return 58;
+        case KeyEvent.VK_A:
+            return 30;
+        case KeyEvent.VK_S:
+            return 31;
+        case KeyEvent.VK_D:
+            return 32;
+        case KeyEvent.VK_F:
+            return 33;
+        case KeyEvent.VK_G:
+            return 34;
+        case KeyEvent.VK_H:
+            return 35;
+        case KeyEvent.VK_J:
+            return 36;
+        case KeyEvent.VK_K:
+            return 37;
+        case KeyEvent.VK_L:
+            return 38;
+        case KeyEvent.VK_SEMICOLON:
+            return 39;
+        case KeyEvent.VK_QUOTE:
+            return 40;
+
+            // Row #4
+        case KeyEvent.VK_SHIFT:
+            switch (event.getKeyLocation()) {
+            default:
+            case KeyEvent.KEY_LOCATION_LEFT:
+                return 42;
+            case KeyEvent.KEY_LOCATION_RIGHT:
+                return 54;
+            }
+        case KeyEvent.VK_BACK_SLASH:
+            return 43;
+        case KeyEvent.VK_Z:
+            return 44;
+        case KeyEvent.VK_X:
+            return 45;
+        case KeyEvent.VK_C:
+            return 46;
+        case KeyEvent.VK_V:
+            return 47;
+        case KeyEvent.VK_B:
+            return 48;
+        case KeyEvent.VK_N:
+            return 49;
+        case KeyEvent.VK_M:
+            return 50;
+        case KeyEvent.VK_COMMA:
+            return 51;
+        case KeyEvent.VK_PERIOD:
+            return 52;
+        case KeyEvent.VK_SLASH:
+            return 53;
+
+            //
+            // Bottom row
+        case KeyEvent.VK_CONTROL:
+            switch (event.getKeyLocation()) {
+            default:
+            case KeyEvent.KEY_LOCATION_LEFT:
+                return 29;
+            case KeyEvent.KEY_LOCATION_RIGHT:
+                return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 29;
+            }
+        case KeyEvent.VK_WINDOWS:
+            switch (event.getKeyLocation()) {
+            default:
+            case KeyEvent.KEY_LOCATION_LEFT:
+                return 91;
+            case KeyEvent.KEY_LOCATION_RIGHT:
+                return 92;
+            }
+        case KeyEvent.VK_ALT:
+            switch (event.getKeyLocation()) {
+            default:
+            case KeyEvent.KEY_LOCATION_LEFT:
+                return 56;
+            case KeyEvent.KEY_LOCATION_RIGHT:
+                return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56;
+            }
+        case KeyEvent.VK_ALT_GRAPH:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56;
+
+        case KeyEvent.VK_SPACE:
+            return 57;
+
+        case KeyEvent.VK_CONTEXT_MENU:
+            return 93;
+
+            //
+            // Special keys
+        case KeyEvent.VK_PRINTSCREEN:
+            return 55;
+        case KeyEvent.VK_SCROLL_LOCK:
+            return 70;
+        case KeyEvent.VK_PAUSE:
+            return 29;
+
+            // Text navigation keys
+        case KeyEvent.VK_INSERT:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 82;
+        case KeyEvent.VK_HOME:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 71;
+        case KeyEvent.VK_PAGE_UP:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 73;
+        case KeyEvent.VK_DELETE:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 83;
+        case KeyEvent.VK_END:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 79;
+        case KeyEvent.VK_PAGE_DOWN:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 81;
+
+            // Cursor keys
+        case KeyEvent.VK_UP:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 72;
+        case KeyEvent.VK_LEFT:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 75;
+        case KeyEvent.VK_DOWN:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 80;
+        case KeyEvent.VK_RIGHT:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 77;
+
+            // Keypad
+        case KeyEvent.VK_NUM_LOCK:
+            return 69;
+        case KeyEvent.VK_DIVIDE:
+            return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 53;
+        case KeyEvent.VK_MULTIPLY:
+            return 55;
+        case KeyEvent.VK_SUBTRACT:
+            return 74;
+        case KeyEvent.VK_ADD:
+            return 78;
+
+        case KeyEvent.VK_NUMPAD7:
+            return 71;
+        case KeyEvent.VK_NUMPAD8:
+            return 72;
+        case KeyEvent.VK_NUMPAD9:
+            return 73;
+        case KeyEvent.VK_NUMPAD4:
+            return 75;
+        case KeyEvent.VK_NUMPAD5:
+            return 76;
+        case KeyEvent.VK_NUMPAD6:
+            return 77;
+        case KeyEvent.VK_NUMPAD1:
+            return 79;
+        case KeyEvent.VK_NUMPAD2:
+            return 80;
+        case KeyEvent.VK_NUMPAD3:
+            return 81;
+        case KeyEvent.VK_NUMPAD0:
+            return 82;
+        case KeyEvent.VK_DECIMAL:
+            return 83;
+
+        default:
+            System.err.println("Key is not mapped: " + event + ".");
+            return 57; // Space
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java
new file mode 100755
index 0000000..6b347aa
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java
@@ -0,0 +1,179 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.adapter;
+
+import java.awt.event.InputEvent;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+import common.MouseOrder;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240594.aspx
+ */
+public class AwtRdpMouseAdapter extends BaseElement {
+    public final static int FASTPATH_INPUT_EVENT_MOUSE = 0x01;
+
+    /**
+     * Event is a mouse wheel rotation. The only valid flags in a wheel rotation
+     * event are PTRFLAGS_WHEEL_NEGATIVE and the WheelRotationMask; all other
+     * pointer flags are ignored.
+     */
+    public final static int PTRFLAGS_WHEEL = 0x0200;
+
+    /**
+     * Wheel rotation value (contained in the WheelRotationMask bit field) is
+     * negative and MUST be sign-extended before injection at the server.
+     */
+    public final static int PTRFLAGS_WHEEL_NEGATIVE = 0x0100;
+
+    /**
+     * Bit field describing the number of rotation units the mouse wheel was
+     * rotated. The value is negative if the PTRFLAGS_WHEEL_NEGATIVE flag is set.
+     */
+    public final static int WHEEL_ROTATION_MASK = 0x01FF;
+
+    /**
+     * Indicates that the mouse position MUST be updated to the location specified
+     * by the xPos and yPos fields.
+     */
+    public final static int PTRFLAGS_MOVE = 0x0800;
+
+    /**
+     * Indicates that a click event has occurred at the position specified by the
+     * xPos and yPos fields. The button flags indicate which button has been
+     * clicked and at least one of these flags MUST be set.
+     */
+    public final static int PTRFLAGS_DOWN = 0x8000;
+
+    /**
+     * Mouse button 1 (left button) was clicked or released. If the PTRFLAGS_DOWN
+     * flag is set, then the button was clicked, otherwise it was released.
+     */
+    public final static int PTRFLAGS_BUTTON1 = 0x1000;
+
+    /**
+     * Mouse button 2 (right button) was clicked or released. If the PTRFLAGS_DOWN
+     * flag is set, then the button was clicked, otherwise it was released.
+     */
+    public final static int PTRFLAGS_BUTTON2 = 0x2000;
+
+    /**
+     * Mouse button 3 (middle button or wheel) was clicked or released. If the
+     * PTRFLAGS_DOWN flag is set, then the button was clicked, otherwise it was
+     * released.
+     */
+    public final static int PTRFLAGS_BUTTON3 = 0x4000;
+
+    public AwtRdpMouseAdapter(String id) {
+        super(id);
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        // Get mouse event
+        MouseOrder order = (MouseOrder)buf.getOrder();
+
+        ByteBuffer outBuf = new ByteBuffer(7, true);
+
+        // eventHeader (1 byte): An 8-bit, unsigned integer. EventCode bitfield (top
+        // 3 bits) MUST be set to FASTPATH_INPUT_EVENT_MOUSE (1). The
+        // eventFlags bitfield (low 5 bits) MUST be zeroed out.
+        outBuf.writeByte(FASTPATH_INPUT_EVENT_MOUSE << 5);
+
+        // pointerFlags (2 bytes): A 16-bit, unsigned integer.
+        outBuf.writeShortLE(getPointerFlags(order));
+
+        // xPos (2 bytes): A 16-bit, unsigned integer. The x-coordinate of the
+        // pointer.
+        outBuf.writeShortLE(order.event.getX());
+
+        // yPos (2 bytes): A 16-bit, unsigned integer. The y-coordinate of the
+        // pointer.
+        outBuf.writeShortLE(order.event.getY());
+
+        // Push buffer to one pad only, so it can be modified without copying of
+        // data
+        pushDataToPad(STDOUT, outBuf);
+    }
+
+    // Remember mouse buttons
+    protected boolean button1, button2, button3;
+
+    protected int getPointerFlags(MouseOrder order) {
+        int flags = 0;
+
+        int modifiers = order.event.getModifiersEx();
+
+        if (order.pressed) {
+            // Mouse pressed
+            flags |= PTRFLAGS_DOWN;
+
+            // Check, which one of buttons is released
+            boolean b1 = ((modifiers & InputEvent.BUTTON1_DOWN_MASK) > 0) && !button1;
+            boolean b2 = ((modifiers & InputEvent.BUTTON2_DOWN_MASK) > 0) && !button2;
+            boolean b3 = ((modifiers & InputEvent.BUTTON3_DOWN_MASK) > 0) && !button3;
+
+            if (b1) {
+                flags |= PTRFLAGS_BUTTON1;
+                button1 = true;
+            }
+
+            if (b2) {
+                flags |= PTRFLAGS_BUTTON3;
+                button2 = true;
+            }
+
+            if (b3) {
+                flags |= PTRFLAGS_BUTTON2;
+                button3 = true;
+            }
+        } else if (order.released) {
+            // Mouse released
+
+            // Check, which one of buttons is released
+            boolean b1 = !((modifiers & InputEvent.BUTTON1_DOWN_MASK) > 0) && button1;
+            boolean b2 = !((modifiers & InputEvent.BUTTON2_DOWN_MASK) > 0) && button2;
+            boolean b3 = !((modifiers & InputEvent.BUTTON3_DOWN_MASK) > 0) && button3;
+
+            if (b1) {
+                flags |= PTRFLAGS_BUTTON1;
+                button1 = false;
+            }
+
+            if (b2) {
+                flags |= PTRFLAGS_BUTTON3;
+                button2 = false;
+            }
+
+            if (b3) {
+                flags |= PTRFLAGS_BUTTON2;
+                button3 = false;
+            }
+        } else {
+            // Mouse moved
+            flags |= PTRFLAGS_MOVE;
+        }
+
+        return flags;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java
new file mode 100755
index 0000000..3a16536
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java
@@ -0,0 +1,143 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import java.util.Map;
+
+import rdpclient.rdp.RdpConstants;
+import streamer.ByteBuffer;
+
+public class ClipboardDataFormat {
+
+    public static final String HTML_FORMAT = "HTML Format";
+    public static final String RTF_AS_TEXT = "RTF As Text";
+    public static final String RICH_TEXT_FORMAT_WITHOUT_OBJECTS = "Rich Text Format Without Objects";
+    public static final String RICH_TEXT_FORMAT = "Rich Text Format";
+
+    public static final int CB_FORMAT_TEXT = 0x0001;
+    public static final int CB_FORMAT_UNICODETEXT = 0x000D;
+
+    /**
+     * Supported clipboard data formats in order of preference.
+     */
+    public static final Object[] supportedTextBasedFormats = new Object[] {
+        // ID's
+        CB_FORMAT_UNICODETEXT, CB_FORMAT_TEXT,
+
+        // Names
+        HTML_FORMAT,
+
+        // RTF_AS_TEXT,
+        // RICH_TEXT_FORMAT_WITHOUT_OBJECTS,
+        // RICH_TEXT_FORMAT,
+
+    };
+
+    public final int id;
+    public final String name;
+
+    public ClipboardDataFormat(int id, String name) {
+        super();
+        this.id = id;
+        this.name = name;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + id;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ClipboardDataFormat other = (ClipboardDataFormat)obj;
+        if (id != other.id)
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "ClipboardDataFormat [id=" + id + ", name=\"" + name + "\"" + ((id == CB_FORMAT_UNICODETEXT) ? " (Unicode text)" : "")
+                + ((id == CB_FORMAT_TEXT) ? " (text)" : "") + "]";
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Parse response of supported format and return it as string.
+     */
+    public String parseServerResponseAsString(ByteBuffer buf) {
+        switch (id) {
+        case CB_FORMAT_UNICODETEXT:
+            return buf.readVariableWideString(RdpConstants.CHARSET_16);
+        case CB_FORMAT_TEXT:
+            return buf.readVariableString(RdpConstants.CHARSET_8);
+        }
+
+        if (name == null || name.length() == 0)
+            return null;
+
+        if (HTML_FORMAT.equals(name))
+            return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify
+
+        // if (RTF_AS_TEXT.equals(name))
+        // return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify
+        //
+        // if (RICH_TEXT_FORMAT_WITHOUT_OBJECTS.equals(name))
+        // return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify
+        //
+        // if (RICH_TEXT_FORMAT.equals(name))
+        // return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify
+
+        return null;
+    }
+
+    /**
+     * Find first (richest) text-based data format.
+     *
+     * @return text-based data format or null, when not found
+     */
+    public static ClipboardDataFormat findBestTextFormat(Map<Object, ClipboardDataFormat> serverClipboardDataFormats) {
+        for (Object formatKey : ClipboardDataFormat.supportedTextBasedFormats)
+            if (serverClipboardDataFormats.containsKey(formatKey))
+                return serverClipboardDataFormats.get(formatKey);
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java
new file mode 100755
index 0000000..9c465b5
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ClipboardState {
+
+    /**
+     * The Long Format Name variant of the Format List PDU is supported for
+     * exchanging updated format names. If this flag is not set, the Short Format
+     * Name variant MUST be used. If this flag is set by both protocol endpoints,
+     * then the Long Format Name variant MUST be used.
+     */
+    public boolean serverUseLongFormatNames = false;
+    public final boolean clientUseLongFormatNames = false;
+
+    /**
+     * File copy and paste using stream-based operations are supported using the
+     * File Contents Request PDU and File Contents Response PDU.
+     */
+    public boolean serverStreamFileClipEnabled = false;
+    public final boolean clientStreamFileClipEnabled = false;
+
+    /**
+     * Indicates that any description of files to copy and paste MUST NOT include
+     * the source path of the files.
+     */
+    public boolean serverFileClipNoFilePaths = false;
+    public final boolean clientFileClipNoFilePaths = false;
+
+    /**
+     * Locking and unlocking of File Stream data on the clipboard is supported
+     * using the Lock Clipboard Data PDU and Unlock Clipboard Data PDU.
+     */
+    public boolean serverCanLockClipdata = false;
+    public final boolean clientCanLockClipdata = false;
+
+    /**
+     * The Monitor Ready PDU is sent from the server to the client to indicate
+     * that the server is initialized and ready.
+     */
+    public boolean serverReady = false;
+
+    /**
+     * Set of data formats, which are supported by server for paste operation.
+     */
+    public Map<Object, ClipboardDataFormat> serverClipboardDataFormats = new HashMap<Object, ClipboardDataFormat>(0);
+
+    /**
+     * Server sends clipboard data in requested format.
+     */
+    public ClipboardDataFormat serverRequestedFormat;
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java
new file mode 100755
index 0000000..3238124
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java
@@ -0,0 +1,193 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class ServerClipRdrChannelRouter extends BaseElement {
+
+    /**
+     * Key for ASCII names message flag in payload metadata.  Value is Boolean.
+     */
+    public static final String ASCII_NAMES = "ascii_names";
+
+    /**
+     * Key for success/fail message flag in payload metadata. Value is Boolean.
+     */
+    public static final String SUCCESS = "success";
+
+    /**
+     * Monitor Ready PDU
+     */
+    public static final int CB_MONITOR_READY = 0x0001;
+
+    /**
+     * Format List PDU
+     */
+    public static final int CB_FORMAT_LIST = 0x0002;
+
+    /**
+     * Format List Response PDU
+     */
+    public static final int CB_FORMAT_LIST_RESPONSE = 0x0003;
+
+    /**
+     * Format Data Request PDU
+     */
+    public static final int CB_FORMAT_DATA_REQUEST = 0x0004;
+
+    /**
+     * Format Data Response PDU
+     */
+    public static final int CB_FORMAT_DATA_RESPONSE = 0x0005;
+
+    /**
+     * Temporary Directory PDU
+     */
+    public static final int CB_TEMP_DIRECTORY = 0x0006;
+
+    /**
+     * Clipboard Capabilities PDU
+     */
+    public static final int CB_CLIP_CAPS = 0x0007;
+
+    /**
+     * File Contents Request PDU
+     */
+    public static final int CB_FILECONTENTS_REQUEST = 0x0008;
+
+    /**
+     * File Contents Response PDU
+     */
+    public static final int CB_FILECONTENTS_RESPONSE = 0x0009;
+
+    /**
+     * Lock Clipboard Data PDU
+     */
+    public static final int CB_LOCK_CLIPDATA = 0x000A;
+
+    /**
+     * Unlock Clipboard Data PDU
+     */
+    public static final int CB_UNLOCK_CLIPDATA = 0x000B;
+
+    /**
+     * Used by the Format List Response PDU, Format Data Response PDU, and File
+     * Contents Response PDU to indicate that the associated request Format List
+     * PDU, Format Data Request PDU, and File Contents Request PDU were processed
+     * successfully.
+     */
+    public static final int CB_RESPONSE_OK = 0x0001;
+
+    /**
+     * Used by the Format List Response PDU, Format Data Response PDU, and File
+     * Contents Response PDU to indicate that the associated Format List PDU,
+     * Format Data Request PDU, and File Contents Request PDU were not processed
+     * successfully.
+     */
+    public static final int CB_RESPONSE_FAIL = 0x0002;
+
+    /**
+     * Used by the Short Format Name variant of the Format List Response PDU to
+     * indicate the format names are in ASCII 8.
+     */
+    public static final int CB_ASCII_NAMES = 0x0004;
+
+    public ServerClipRdrChannelRouter(String id) {
+        super(id);
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        // Parse PDU header
+        // Example: 07 00 -> CLIPRDR_HEADER::msgType = CB_CLIP_CAPS (7)
+        int msgType = buf.readUnsignedShortLE();
+
+        // Example: 00 00 -> CLIPRDR_HEADER::msgFlags = 0
+        int msgFlags = buf.readUnsignedShortLE();
+
+        // Example: 10 00 00 00 -> CLIPRDR_HEADER::dataLen = 0x10 = 16 bytes
+        long dataLenLong = buf.readSignedIntLE();
+        if (dataLenLong > 4 * 1024 * 1024)
+            throw new RuntimeException("Clipboard packet is too long. Expected length: less than 4MiB. Actual length: " + dataLenLong + ".");
+        int dataLen = (int)dataLenLong;
+
+        ByteBuffer payload = buf.readBytes(dataLen);
+
+        // Parse message flags and store them in the payload metadata
+        if ((msgFlags & CB_RESPONSE_OK) == CB_RESPONSE_OK)
+            payload.putMetadata("success", true);
+        if ((msgFlags & CB_RESPONSE_FAIL) == CB_RESPONSE_FAIL)
+            payload.putMetadata(SUCCESS, false);
+        if ((msgFlags & CB_ASCII_NAMES) == CB_ASCII_NAMES)
+            payload.putMetadata(ASCII_NAMES, true);
+
+        // Push PDU to appropriate handler
+        switch (msgType) {
+        case CB_MONITOR_READY:
+            pushDataToPad("monitor_ready", payload);
+            break;
+        case CB_FORMAT_LIST:
+            pushDataToPad("format_list", payload);
+            break;
+        case CB_FORMAT_LIST_RESPONSE:
+            pushDataToPad("format_list_response", payload);
+            break;
+        case CB_FORMAT_DATA_REQUEST:
+            pushDataToPad("format_data_request", payload);
+            break;
+        case CB_FORMAT_DATA_RESPONSE:
+            pushDataToPad("format_data_response", payload);
+            break;
+        case CB_TEMP_DIRECTORY:
+            throw new RuntimeException("[" + this + "] ERROR: Unexpected clipboard temporary directory PDU received from server. Data: " + buf + ".");
+        case CB_CLIP_CAPS:
+            pushDataToPad("clipboard_capabilities", payload);
+            break;
+        case CB_FILECONTENTS_REQUEST:
+            pushDataToPad("filecontent_request", payload);
+            break;
+        case CB_FILECONTENTS_RESPONSE:
+            pushDataToPad("filecontent_response", payload);
+            break;
+        case CB_LOCK_CLIPDATA:
+            pushDataToPad("lock_clipdata", payload);
+            break;
+        case CB_UNLOCK_CLIPDATA:
+            pushDataToPad("unlock_clipdata", payload);
+            break;
+        default:
+            throw new RuntimeException("[" + this + "] ERROR: Unknown clipboard PDU message type: " + msgType + ".");
+        }
+
+        buf.unref();
+
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String args[]) {
+        // TODO
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java
new file mode 100755
index 0000000..68278e8
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java
@@ -0,0 +1,180 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+public class ServerClipboardCapabilitiesPDU extends BaseElement {
+
+    /**
+     * General capability set.
+     */
+    public static final int CB_CAPSTYPE_GENERAL = 0x1;
+
+    /**
+     * The Long Format Name variant of the Format List PDU is supported for
+     * exchanging updated format names. If this flag is not set, the Short Format
+     * Name variant MUST be used. If this flag is set by both protocol endpoints,
+     * then the Long Format Name variant MUST be used.
+     */
+    public static final int CB_USE_LONG_FORMAT_NAMES = 0x00000002;
+
+    /**
+     * File copy and paste using stream-based operations are supported using the
+     * File Contents Request PDU and File Contents Response PDU.
+     */
+    public static final int CB_STREAM_FILECLIP_ENABLED = 0x00000004;
+
+    /**
+     * Indicates that any description of files to copy and paste MUST NOT include
+     * the source path of the files.
+     */
+    public static final int CB_FILECLIP_NO_FILE_PATHS = 0x00000008;
+
+    /**
+     * Locking and unlocking of File Stream data on the clipboard is supported
+     * using the Lock Clipboard Data PDU and Unlock Clipboard Data PDU.
+     */
+    public static final int CB_CAN_LOCK_CLIPDATA = 0x00000010;
+
+    protected ClipboardState state;
+
+    public ServerClipboardCapabilitiesPDU(String id, ClipboardState state) {
+        super(id);
+        this.state = state;
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        // 0x01, 0x00, // CLIPRDR_CAPS::cCapabilitiesSets = 1
+        int cCapabilitiesSets = buf.readUnsignedShortLE();
+
+        // 0x00, 0x00, // CLIPRDR_CAPS::pad1
+        buf.skipBytes(2);
+
+        // Parse all capability sets
+        for (int capabilitySet = 0; capabilitySet < cCapabilitiesSets; capabilitySet++) {
+            // 0x01, 0x00, // CLIPRDR_CAPS_SET::capabilitySetType =
+            // CB_CAPSTYPE_GENERAL (1)
+            int capabilitySetType = buf.readUnsignedShortLE();
+
+            // 0x0c, 0x00, // CLIPRDR_CAPS_SET::lengthCapability = 0x0c = 12 bytes
+            int lengthCapability = buf.readUnsignedShortLE();
+
+            // parse capability set
+            switch (capabilitySetType) {
+            case CB_CAPSTYPE_GENERAL:
+                parseGeneralCapabilitySet(buf.readBytes(lengthCapability - 4));
+                break;
+            default:
+                // Ignore
+                // throw new RuntimeException("Unknown capability set type: " +
+                // capabilitySetType + ". Expected value: CB_CAPSTYPE_GENERAL (1).");
+            }
+        }
+
+        buf.unref();
+    }
+
+    protected void parseGeneralCapabilitySet(ByteBuffer buf) {
+        // 0x02, 0x00, 0x00, 0x00, // CLIPRDR_GENERAL_CAPABILITY::version =
+        // CB_CAPS_VERSION_2 (2)
+        // long version = buf.readUnsignedIntLE();
+        buf.skipBytes(4);
+
+        // 0x0e, 0x00, 0x00, 0x00, // CLIPRDR_GENERAL_CAPABILITY::capabilityFlags
+        // = 0x0000000e = 0x02 |0x04 |0x08 = CB_USE_LONG_FORMAT_NAMES |
+        // CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS
+        int flags = buf.readSignedIntLE();
+
+        if ((flags & CB_USE_LONG_FORMAT_NAMES) == CB_USE_LONG_FORMAT_NAMES) {
+            state.serverUseLongFormatNames = true;
+            if (verbose)
+                System.out.println("[" + this + "] INFO: Server can use long format names for clipboard data.");
+        }
+
+        if ((flags & CB_STREAM_FILECLIP_ENABLED) == CB_STREAM_FILECLIP_ENABLED) {
+            state.serverStreamFileClipEnabled = true;
+            if (verbose)
+                System.out.println("[" + this + "] INFO: Server supports stream based file clipboard operations.");
+        }
+
+        if ((flags & CB_FILECLIP_NO_FILE_PATHS) == CB_FILECLIP_NO_FILE_PATHS) {
+            state.serverFileClipNoFilePaths = true;
+            if (verbose)
+                System.out.println("[" + this
+                        + "] INFO: Server Indicates that any description of files to copy and paste MUST NOT include the source path of the files.");
+        }
+
+        if ((flags & CB_CAN_LOCK_CLIPDATA) == CB_CAN_LOCK_CLIPDATA) {
+            state.serverCanLockClipdata = true;
+            if (verbose)
+                System.out.println("[" + this + "] INFO: Server can lock and unlock file streams on the clipboard.");
+        }
+
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String[] args) {
+        // System.setProperty("streamer.Link.debug", "true");
+        System.setProperty("streamer.Element.debug", "true");
+        // System.setProperty("streamer.Pipeline.debug", "true");
+
+        /* @formatter:off */
+        byte[] packet = new byte[] {
+                0x07, 0x00,  //  CLIPRDR_HEADER::msgType = CB_CLIP_CAPS (7)
+                0x00, 0x00,  //  CLIPRDR_HEADER::msgFlags = 0
+                0x10, 0x00, 0x00, 0x00,  //  CLIPRDR_HEADER::dataLen = 0x10 = 16 bytes
+                0x01, 0x00,  //  CLIPRDR_CAPS::cCapabilitiesSets = 1
+                0x00, 0x00,  //  CLIPRDR_CAPS::pad1
+                0x01, 0x00,  //  CLIPRDR_CAPS_SET::capabilitySetType = CB_CAPSTYPE_GENERAL (1)
+                0x0c, 0x00,  //  CLIPRDR_CAPS_SET::lengthCapability = 0x0c = 12 bytes
+                0x02, 0x00, 0x00, 0x00,  //  CLIPRDR_GENERAL_CAPABILITY::version = CB_CAPS_VERSION_2 (2)
+                0x0e, 0x00, 0x00, 0x00,  //  CLIPRDR_GENERAL_CAPABILITY::capabilityFlags = 0x0000000e = 0x02 |0x04 |0x08 = CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS
+        };
+        /* @formatter:on */
+
+        MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+        Element router = new ServerClipRdrChannelRouter("router");
+        ClipboardState state = new ClipboardState();
+        Element clip_cap = new ServerClipboardCapabilitiesPDU("clip_cap", state);
+        Element sink = new MockSink("sink", new ByteBuffer[] {});
+
+        Pipeline pipeline = new PipelineImpl("test");
+        pipeline.add(source, router, clip_cap, sink);
+        pipeline.link("source", "router >clipboard_capabilities", "clip_cap", "sink");
+        pipeline.runMainLoop("source", STDOUT, false, false);
+
+        // Check state
+        if (!state.serverUseLongFormatNames || !state.serverStreamFileClipEnabled || !state.serverFileClipNoFilePaths || state.serverCanLockClipdata)
+            throw new RuntimeException("Server clipboard capabilities packet parsed incorrectly.");
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java
new file mode 100755
index 0000000..99f9eda
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java
@@ -0,0 +1,97 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+import common.adapter.AwtClipboardAdapter;
+
+public class ServerFormatDataResponsePDU extends BaseElement {
+
+    public static final String SERVER_CLIPBOARD_ADAPTER_PAD = "clipboard";
+
+    protected ClipboardState state;
+
+    public ServerFormatDataResponsePDU(String id, ClipboardState state) {
+        super(id);
+        this.state = state;
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        parseData(buf);
+
+        buf.unref();
+    }
+
+    protected void parseData(ByteBuffer buf) {
+
+        String content = state.serverRequestedFormat.parseServerResponseAsString(buf);
+
+        // Send response to clipboard adapter
+        ByteBuffer outBuf = new ByteBuffer(0);
+        outBuf.putMetadata(AwtClipboardAdapter.CLIPBOARD_CONTENT, content);
+
+        pushDataToPad(SERVER_CLIPBOARD_ADAPTER_PAD, outBuf);
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String[] args) {
+        // System.setProperty("streamer.Link.debug", "true");
+        System.setProperty("streamer.Element.debug", "true");
+        // System.setProperty("streamer.Pipeline.debug", "true");
+
+        /* @formatter:off */
+        byte[] packet = new byte[] {
+                0x05, 0x00,  //  CLIPRDR_HEADER::msgType = CB_FORMAT_DATA_RESPONSE (5)
+                0x01, 0x00,  //  CLIPRDR_HEADER::msgFlags = 0x0001 = CB_RESPONSE_OK
+                0x18, 0x00, 0x00, 0x00,  //  CLIPRDR_HEADER::dataLen = 0x18 = 24 bytes
+
+                0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x77, 0x00, 0x6f, 0x00,
+                0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x00, 0x00,  //  CLIPRDR_FORMAT_DATA_RESPONSE::requestedFormatData: "hello world"
+        };
+        /* @formatter:on */
+
+        MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+        Element router = new ServerClipRdrChannelRouter("router");
+        ClipboardState state = new ClipboardState();
+        state.serverRequestedFormat = new ClipboardDataFormat(ClipboardDataFormat.CB_FORMAT_UNICODETEXT, "");
+        Element format_data_response = new ServerFormatDataResponsePDU("format_data_response", state);
+
+        ByteBuffer clipboardAdapterPacket = new ByteBuffer(0);
+        clipboardAdapterPacket.putMetadata(AwtClipboardAdapter.CLIPBOARD_CONTENT, "hello world");
+        Element sink = new MockSink("sink", new ByteBuffer[] {clipboardAdapterPacket});
+
+        Pipeline pipeline = new PipelineImpl("test");
+        pipeline.add(source, router, format_data_response, sink);
+        pipeline.link("source", "router >format_data_response", "format_data_response >clipboard", "sink");
+        pipeline.runMainLoop("source", STDOUT, false, false);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java
new file mode 100755
index 0000000..776f451
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java
@@ -0,0 +1,237 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import rdpclient.rdp.RdpConstants;
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+public class ServerFormatListPDU extends BaseElement {
+
+    protected ClipboardState state;
+
+    public ServerFormatListPDU(String id, ClipboardState state) {
+        super(id);
+        this.state = state;
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        parseFormatNames(buf);
+        buf.unref();
+
+        // Automatically send request for text-based data to insert it into local
+        // clipboard
+        ClipboardDataFormat textFormat = ClipboardDataFormat.findBestTextFormat(state.serverClipboardDataFormats);
+        if (textFormat != null) {
+            // Send response: OK
+            sendFormatListParseResponse(true);
+            // Request data
+            sendFormatDataRequest(textFormat);
+        } else {
+            // Send response: FAIL, we are not interested in this data
+            sendFormatListParseResponse(false);
+        }
+    }
+
+    /**
+     * The Format Data Request PDU is sent by the recipient of the Format List
+     * PDU. It is used to request the data for one of the formats that was listed
+     * in the Format List PDU.
+     */
+    protected void sendFormatDataRequest(ClipboardDataFormat textFormat) {
+
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Sending request for data in following format: " + textFormat + ".");
+
+        // Store data format to parse server response later
+        state.serverRequestedFormat = textFormat;
+
+        ByteBuffer buf = new ByteBuffer(12, true);
+
+        // Type
+        buf.writeShortLE(ServerClipRdrChannelRouter.CB_FORMAT_DATA_REQUEST);
+        // Message flags
+        buf.writeShortLE(0);
+        // Length
+        buf.writeIntLE(4);
+
+        // ID of chosen format
+        buf.writeIntLE(textFormat.id);
+
+        buf.trimAtCursor();
+
+        pushDataToPad(STDOUT, buf);
+    }
+
+    /**
+     * The Format List Response PDU is sent as a reply to the Format List PDU. It
+     * is used to indicate whether processing of the Format List PDU was
+     * successful.
+     *
+     * @param b
+     */
+    protected void sendFormatListParseResponse(boolean ok) {
+        ByteBuffer buf = new ByteBuffer(8, true);
+
+        // Type
+        buf.writeShortLE(ServerClipRdrChannelRouter.CB_FORMAT_LIST_RESPONSE);
+        // Message flags
+        buf.writeShortLE((ok) ? ServerClipRdrChannelRouter.CB_RESPONSE_OK : ServerClipRdrChannelRouter.CB_RESPONSE_FAIL);
+        // Length
+        buf.writeIntLE(0);
+
+        buf.trimAtCursor();
+
+        pushDataToPad(STDOUT, buf);
+    }
+
+    protected void parseFormatNames(ByteBuffer buf) {
+
+        // Set will not be modified after creation, so there is no need to make it
+        // synchronous.
+        Map<Object, ClipboardDataFormat> formats = new HashMap<Object, ClipboardDataFormat>();
+
+        while (buf.cursor < buf.length) {
+            int id = buf.readSignedIntLE();
+
+            String name;
+            if (state.serverUseLongFormatNames) {
+                // Long format names in Unicode
+                name = buf.readVariableWideString(RdpConstants.CHARSET_16);
+            } else {
+                Boolean asciiNames = (Boolean)buf.getMetadata(ServerClipRdrChannelRouter.ASCII_NAMES);
+
+                if (asciiNames != null && asciiNames) {
+                    // Short format names in ASCII
+                    name = buf.readString(32, RdpConstants.CHARSET_8);
+                } else {
+                    // Short format names in Unicode
+                    name = buf.readString(32, RdpConstants.CHARSET_16);
+                }
+
+            }
+
+            // Store format in map by both ID and name (if name is not empty)
+            formats.put(id, new ClipboardDataFormat(id, name));
+            if (name.length() > 0)
+                formats.put(name, new ClipboardDataFormat(id, name));
+        }
+
+        if (verbose)
+            System.out.println("Server supports following formats for clipboard data: " + formats.values().toString() + ".");
+
+        state.serverClipboardDataFormats = formats;
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String[] args) {
+        // System.setProperty("streamer.Link.debug", "true");
+        System.setProperty("streamer.Element.debug", "true");
+        // System.setProperty("streamer.Pipeline.debug", "true");
+
+        /* @formatter:off */
+        byte[] packet = new byte[] {
+                0x02, 0x00,  //  CLIPRDR_HEADER::msgType = CB_FORMAT_LIST (2)
+                0x00, 0x00,  //  CLIPRDR_HEADER::msgFlags = 0
+                (byte) 0xe0, 0x00, 0x00, 0x00,  //  CLIPRDR_HEADER::dataLen = 0xe0 = 224 bytes
+
+                (byte) 0x8a, (byte) 0xc0, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc08a = 49290
+                0x52, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x54, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = "Rich Text Format"
+
+                0x45, (byte) 0xc1, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc145 = 49477
+                0x52, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x54, 0x00, 0x65, 0x00, 0x78, 0x00,
+                0x74, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00,
+                0x20, 0x00, 0x57, 0x00, 0x69, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00,
+                0x20, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x6a, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x73, 0x00,
+                0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = "Rich Text Format Without Objects"
+
+                0x43, (byte) 0xc1, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc143 = 49475
+                0x52, 0x00, 0x54, 0x00, 0x46, 0x00, 0x20, 0x00, 0x41, 0x00, 0x73, 0x00, 0x20, 0x00, 0x54, 0x00,
+                0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = "RTF As Text"
+
+                0x01, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 1
+                0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = ""
+
+                0x0d, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 0x0d = 13
+                0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = ""
+
+                0x04, (byte) 0xc0, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc004 = 49156
+                0x4e, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x00, 0x00,  //  "Native"
+
+                0x0e, (byte) 0xc0, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc00e = 49166
+                0x4f, 0x00, 0x62, 0x00, 0x6a, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00, 0x44, 0x00,
+                0x65, 0x00, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x6f, 0x00,
+                0x72, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = "Object Descriptor"
+
+                0x03, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 3
+                0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = ""
+
+                0x10, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 16
+                0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = ""
+
+                0x07, 0x00, 0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatId = 7
+                0x00, 0x00,  //  CLIPRDR_LONG_FORMAT_NAME::formatName = ""
+        };
+        /* @formatter:on */
+
+        MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+        Element router = new ServerClipRdrChannelRouter("router");
+        ClipboardState state = new ClipboardState();
+        state.serverUseLongFormatNames = true;
+        Element format_list = new ServerFormatListPDU("format_list", state);
+
+        Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+                // Format List Response PDU
+                0x03, 0x00, // CLIPRDR_HEADER::msgType = CB_FORMAT_LIST_RESPONSE (3)
+                0x01, 0x00, // CLIPRDR_HEADER::msgFlags = 0x0001 = CB_RESPONSE_OK
+                0x00, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0 bytes
+        }, new byte[] {
+                // Format Data Request PDU
+                0x04, 0x00, // CLIPRDR_HEADER::msgType = CB_FORMAT_DATA_REQUEST (4)
+                0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0
+                0x04, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 4 bytes
+                0x0d, 0x00, 0x00, 0x00, // CLIPRDR_FORMAT_DATA_REQUEST::requestedFormatId
+                // = 0x0d
+        }));
+
+        Pipeline pipeline = new PipelineImpl("test");
+        pipeline.add(source, router, format_list, sink);
+        pipeline.link("source", "router >format_list", "format_list", "sink");
+        pipeline.runMainLoop("source", STDOUT, false, false);
+
+        // Check state
+        if (!(state.serverClipboardDataFormats.containsKey(49475) && state.serverClipboardDataFormats.containsKey("Rich Text Format")))
+            throw new RuntimeException("Server format list packet parsed incorrectly.");
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java
new file mode 100755
index 0000000..bbc356e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.clip;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.MockSink;
+import streamer.debug.MockSource;
+
+public class ServerMonitorReadyPDU extends BaseElement {
+
+    protected ClipboardState state;
+
+    public ServerMonitorReadyPDU(String id, ClipboardState state) {
+        super(id);
+        this.state = state;
+    }
+
+    // 0x01, 0x00, // CLIPRDR_HEADER::msgType = CB_MONITOR_READY (1)
+    // 0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0
+    // 0x00, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0 bytes
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        state.serverReady = true;
+
+        buf.unref();
+
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String[] args) {
+        // System.setProperty("streamer.Link.debug", "true");
+        System.setProperty("streamer.Element.debug", "true");
+        // System.setProperty("streamer.Pipeline.debug", "true");
+
+        /* @formatter:off */
+        byte[] packet = new byte[] {
+                0x01, 0x00,  //  CLIPRDR_HEADER::msgType = CB_MONITOR_READY (1)
+                0x00, 0x00,  //  CLIPRDR_HEADER::msgFlags = 0
+                0x00, 0x00, 0x00, 0x00,  //  CLIPRDR_HEADER::dataLen = 0 bytes
+        };
+        /* @formatter:on */
+
+        MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+        Element router = new ServerClipRdrChannelRouter("router");
+        ClipboardState state = new ClipboardState();
+        Element monitor_ready = new ServerMonitorReadyPDU("monitor_ready", state);
+        Element sink = new MockSink("sink", new ByteBuffer[] {});
+
+        Pipeline pipeline = new PipelineImpl("test");
+        pipeline.add(source, router, monitor_ready, sink);
+        pipeline.link("source", "router >monitor_ready", "monitor_ready", "sink");
+        pipeline.runMainLoop("source", STDOUT, false, false);
+
+        // Check state
+        if (!state.serverReady)
+            throw new RuntimeException("Server monitor ready packet parsed incorrectly.");
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java
new file mode 100755
index 0000000..203fde4
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java
@@ -0,0 +1,51 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.debug;
+
+/**
+ * Try to determine packet content by it header fingerprint.
+ */
+public class ClientPacketSniffer extends PacketSniffer {
+
+    private static final Pair[] clientRegexps = new Pair[] {
+// @formatter:off
+        new Pair("Client FastPath input",           "04"),
+        new Pair("Client X224ConnectionRequest",    "03 00 XX XX 27 E0"),
+        new Pair("Client ConnectionRequest",        "03 00 XX XX XX E0"),
+        new Pair("Client MCConnectInitial",         "03 00 XX XX 02 F0 80 7F 65"),
+        new Pair("Client ErectDomainRequest",       "03 00 XX XX 02 F0 80 04"),
+        new Pair("Client AttachUserRequest",        "03 00 XX XX 02 F0 80 28"),
+        new Pair("Client ChannelJoinRequest",       "03 00 XX XX 02 F0 80 38"),
+        new Pair("Client Info",                     "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 00 00"),
+        new Pair("Client ConfirmActivePDU",         "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 13 00"),
+        new Pair("Client SynchronizePDU",           "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 1F"),
+        new Pair("Client ControlPDU",               "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 14"),
+        new Pair("Client FontListPDU",              "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 27"),
+        new Pair("Client BitmapCachePersistentList","03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX XX XX XX XX XX 2b"),
+        new Pair("Client CredSSP",                  "30"),
+        new Pair("Client HyperV PreConnection Blob","5E"),
+        new Pair("Client a TPKT packet",            "03"),
+        new Pair("Client a Fast Path packet",       "00"),
+        // @formatter:on
+
+    };
+
+    public ClientPacketSniffer(String id) {
+        super(id, clientRegexps);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/48c47101/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java
new file mode 100755
index 0000000..efbe689
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java
@@ -0,0 +1,75 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package rdpclient.debug;
+
+import java.util.regex.Pattern;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+/**
+ * Try to determine packet content by it header fingerprint.
+ */
+public class PacketSniffer extends BaseElement {
+
+    protected Pair regexps[] = null;
+
+    public PacketSniffer(String id, Pair[] regexps) {
+        super(id);
+        this.regexps = regexps;
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+
+        matchPacket(buf);
+
+        super.handleData(buf, link);
+    }
+
+    private void matchPacket(ByteBuffer buf) {
+        String header = buf.toPlainHexString(100);
+        for (Pair pair : regexps) {
+            if (pair.regexp.matcher(header).find()) {
+                System.out.println("[" + this + "] INFO: Packet: " + pair.name + ".");
+                return;
+            }
+        }
+
+        System.out.println("[" + this + "] INFO: Unknown packet: " + header + ".");
+    }
+
+    protected static class Pair {
+        String name;
+        Pattern regexp;
+
+        protected Pair(String name, String regexp) {
+            this.name = name;
+            this.regexp = Pattern.compile("^" + replaceShortcuts(regexp), Pattern.CASE_INSENSITIVE);
+        }
+
+        private static String replaceShortcuts(String regexp) {
+            String result = regexp;
+            result = result.replaceAll("XX\\*", "([0-9a-fA-F]{2} )*?");
+            result = result.replaceAll("XX\\?", "([0-9a-fA-F]{2} )?");
+            result = result.replaceAll("XX", "[0-9a-fA-F]{2}");
+            return result;
+        }
+    }
+
+}