You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by de...@apache.org on 2013/11/08 09:16:04 UTC

[1/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Updated Branches:
  refs/heads/master 57ba367f3 -> a98c473dc


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java
new file mode 100644
index 0000000..323380b
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java
@@ -0,0 +1,115 @@
+// 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 vncclient;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import streamer.ByteBuffer;
+import streamer.InputStreamSource;
+import streamer.Link;
+import streamer.OneTimeSwitch;
+import streamer.OutputStreamSink;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+/**
+ * VNC server sends hello packet with RFB protocol version, e.g.
+ * "RFB 003.007\n". We need to send response packet with supported protocol
+ * version, e.g. "RFB 003.003\n".
+ */
+public class Vnc_3_3_Hello extends OneTimeSwitch {
+
+  public Vnc_3_3_Hello(String id) {
+    super(id);
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    // Initial packet is exactly 12 bytes long
+    if (!cap(buf, 12, 12, link, false))
+      return;
+
+    // Read protocol version
+    String rfbProtocol = new String(buf.data, buf.offset, buf.length, RfbConstants.US_ASCII_CHARSET);
+    buf.unref();
+
+    // Server should use RFB protocol 3.x
+    if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR))
+      throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
+
+    // Send response: we support RFB 3.3 only
+    String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n";
+
+    ByteBuffer outBuf = new ByteBuffer(ourProtocolString.getBytes(RfbConstants.US_ASCII_CHARSET));
+
+    if (verbose) {
+      outBuf.putMetadata("sender", this);
+      outBuf.putMetadata("version", RfbConstants.RFB_PROTOCOL_VERSION);
+    }
+
+    pushDataToOTOut(outBuf);
+
+    // Switch off this element from circuit
+    switchOff();
+
+  }
+
+  public String toString() {
+    return "Vnc3.3 Hello(" + id + ")";
+  }
+
+  /**
+   * 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");
+
+    InputStream is = new ByteArrayInputStream("RFB 003.007\ntest".getBytes(RfbConstants.US_ASCII_CHARSET));
+    ByteArrayOutputStream initOS = new ByteArrayOutputStream();
+    ByteArrayOutputStream mainOS = new ByteArrayOutputStream();
+    InputStreamSource inputStreamSource = new InputStreamSource("source", is);
+    OutputStreamSink outputStreamSink = new OutputStreamSink("mainSink", mainOS);
+
+    Vnc_3_3_Hello hello = new Vnc_3_3_Hello("hello");
+
+    Pipeline pipeline = new PipelineImpl("test");
+
+    pipeline.addAndLink(inputStreamSource, hello, outputStreamSink);
+    pipeline.add(new OutputStreamSink("initSink", initOS));
+
+    pipeline.link("hello >" + OneTimeSwitch.OTOUT, "initSink");
+
+    pipeline.runMainLoop("source", STDOUT, false, false);
+
+    String initOut = new String(initOS.toByteArray(), RfbConstants.US_ASCII_CHARSET);
+    String mainOut = new String(mainOS.toByteArray(), RfbConstants.US_ASCII_CHARSET);
+
+    if (!"RFB 003.003\n".equals(initOut))
+      System.err.println("Unexpected value for hello response: \"" + initOut + "\".");
+
+    if (!"test".equals(mainOut))
+      System.err.println("Unexpected value for main data: \"" + mainOut + "\".");
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
new file mode 100644
index 0000000..dd41683
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
@@ -0,0 +1,32 @@
+// 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.
+
+To debug RDP sessions with Network Monitor or Wireshark, you need to
+configure RDP server with custom private key. For Network Monitor
+Decrypt Expert, you also will need to downgrade RDP server TLS protocol
+to version 1.0.
+
+File dev-rdp-config.bat contains instructions to configure RDP to use custom
+key, open firewall, disable NLA, downgrade TLS, and start RDP service.
+
+File rdp.pfx contains custom private key (password: test) for use with
+rdp-config.bat and Network Monitor Decrypt Expert. If you will generate
+your own key, you will need to alter rpd-file.bat to use it
+fingerprints.
+
+File rdp-key.pem contains private key in PEM format for use with
+Wireshark.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
new file mode 100644
index 0000000..14a7bbd
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
@@ -0,0 +1,126 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+
+rem 
+rem Configure and start RDP service.
+rem Configure RPD service to use custom key instead of autogenerated for Wireshark and Network Monitor Decrypt Expert.
+rem rdp.pfx is necessary because it fingerprints are hardcoded in this script.
+rem 
+
+rem Turn off firewall
+
+netsh advfirewall firewall set rule group="Remote Desktop" new enable=yes
+
+rem Enable TS connections
+rem 
+rem Windows Registry Editor Version 5.00
+rem 
+rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
+rem "AllowTSConnections"=dword:00000001
+rem "fDenyTSConnections"=dword:00000000
+
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "AllowTSConnections" /t REG_DWORD /d 1 /f
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "fDenyTSConnections" /t REG_DWORD /d 0 /f
+
+rem Disable RDP NLA
+
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0 /f
+
+rem Enable TS service
+
+sc config TermService start=auto
+
+rem Certificate Generation
+
+rem Make self-signed certificate
+
+rem makecert -r -pe -n "CN=%COMPUTERNAME%" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr LocalMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
+
+rem Import certificate
+
+certutil -p test -importPFX "Remote Desktop" rdp.pfx
+
+rem Configure RDP server to use certificate:
+
+rem Windows Registry Editor Version 5.00
+rem 
+rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]
+rem "SSLCertificateSHA1Hash"=hex:c1,70,84,70,bc,56,42,0a,bb,f4,35,35,ba,a6,09,b0,4e,98,4a,47
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v "SSLCertificateSHA1Hash" /t REG_HEX /d "" /f
+
+rem  Grant permissions on certificate for everyone
+
+rem  certutil -repairstore My "bcb40fb84ac891bd41068fe686864559" D:PAI(A;;GA;;;BA)(A;;GA;;;SY)(A;;GR;;;NS)
+certutil -repairstore "Remote Desktop" "bcb40fb84ac891bd41068fe686864559" D:PAI(A;;GA;;;BA)(A;;GA;;;SY)(A;;GR;;;NS)
+
+rem confirm with
+
+rem certutil -store -v My
+certutil -store -v "Remote Desktop"
+
+rem Disable TLS 1.1 (for Network Monitor Decrypt Expert)
+rem 
+rem Windows Registry Editor Version 5.00
+rem 
+rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client]
+rem "Enabled"=dword:00000000
+rem "DisabledByDefault"=dword:00000001
+rem 
+rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server]
+rem "Enabled"=dword:00000000
+rem "DisabledByDefault"=dword:00000001
+
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client" /v "Enabled" /t REG_DWORD /d 0 /f
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client" /v "DisabledByDefault" /t REG_DWORD /d 1 /f
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" /v "Enabled" /t REG_DWORD /d 0 /f
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" /v "DisabledByDefault" /t REG_DWORD /d 1 /f
+
+
+rem Disable TLS 1.2 (for Network Monitor Decrypt Expert)
+rem 
+rem Windows Registry Editor Version 5.00
+rem 
+rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
+rem "Enabled"=dword:00000000
+rem "DisabledByDefault"=dword:00000001
+rem 
+rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server]
+rem "Enabled"=dword:00000000
+rem "DisabledByDefault"=dword:00000001
+
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client" /v "Enabled" /t REG_DWORD /d 0 /f
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client" /v "DisabledByDefault" /t REG_DWORD /d 1 /f
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" /v "Enabled" /t REG_DWORD /d 0 /f
+reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" /v "DisabledByDefault" /t REG_DWORD /d 1 /f
+
+rem Start TS service
+
+net start Termservice
+
+
+rem For Network Monitor Decrypt Expert.
+
+rem Install .Net 3.5
+
+rem dism /online /enable-feature /featurename:NetFx3ServerFeatures
+rem dism /online /enable-feature /featurename:NetFx3
+
+rem PS.
+rem Don't forget to set Windows profile as active in Network Monitor, so SSL traffic branch will appear under
+rem svnchost.exe, so you will be able to decrypt it (don't forget to save and reopen captured traffic to file first).
+rem 
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem b/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem
new file mode 100644
index 0000000..cd050cd
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem
@@ -0,0 +1,23 @@
+Bag Attributes
+    Microsoft Local Key set: <No Values>
+    localKeyID: 01 00 00 00 
+    friendlyName: 8fcf718d-921f-4bfc-9ae4-f63e9c66b6c7
+    Microsoft CSP Name: Microsoft RSA SChannel Cryptographic Provider
+Key Attributes
+    X509v3 Key Usage: 10 
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKrmMXjeoXRn6UFf
+Hmw2HOnT/mEeSWQANzquJnKDBORIjD2rxL3h5FQ/DQUF4gm5JvBll8uWpDX11mVm
+LlAZ2kA8KuJ2JuYEvu/GwuDyrP4D1sOiCwIjJnvmg5DjLB9sll5ohMbjMtiSFm5L
+/YJNXop/pGJucvVzL4t0ZJ1zT2lZAgMBAAECgYAN/OJeuyyQeD+iXweaVTS5OzJ7
+PrBEgM03pQpp9zXx/P6LJUe1c2UUM8bvVGoJ+eW2HNkES/oSN2MLEKAVl7aCLWTe
+7Ejc3JIRB7ZRdNt89w9XvxuRSn87pO082ciMsLvEqqDYahy3BxgI0J/GKbo28Zme
+Z9f9QNCZ8TzbXJbDmwJBANVpBSfi09n5fUy3nGurGxE3chBnyik+Rft63fZp9eiD
+lU5Q4l22+ZUTBChJUtLHztihcb4a4RQX6B4nH5Y1RtMCQQDNAVBKe2VfnFeEoIX7
+ooRnIKIVMxW08GENuJz64otshfH6jRaLL4E/QJLIpoNRFqafyuMkP5x8oZ3uvV1+
+nsujAkAd0Xez9ACP00lLn9gOPzEf/bRFUIsxqg7TLX64AGQoocIJ2ElYuMk0qByL
+mHsnEl33bM9ctZq/WPvIwsSqEzWbAkAcb/k2S8W1LJfLUwUi8dlSAOna7Pou3kVo
+RNqpxrE2faIicl3VMuLH5mo2ITsIDY9RjTBS/+vyMe0Zh/UnMlnnAkAAppLiJ15o
+L3JlVbGRN+4kCP2HVtVRVIl3OlBoVSJZ5qe+s7HowTGurU/iYr1kmWd/C5sU0KPB
+evwz8pdL08vr
+-----END PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java
new file mode 100644
index 0000000..cba01fd
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java
@@ -0,0 +1,189 @@
+// 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 static rdpclient.MockServer.Packet.PacketType.CLIENT;
+import static rdpclient.MockServer.Packet.PacketType.SERVER;
+import static rdpclient.MockServer.Packet.PacketType.UPGRADE_TO_SSL;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import junit.framework.TestCase;
+import rdpclient.MockServer.Packet;
+
+public class MockServerTest extends TestCase {
+
+    public void testIsMockServerCanRespond() throws Exception {
+
+	final byte[] mockClientData = new byte[] { 0x01, 0x02, 0x03 };
+	final byte[] mockServerData = new byte[] { 0x03, 0x02, 0x01 };
+
+	MockServer server = new MockServer(new Packet[] { new Packet("Client hello") {
+	    {
+		type = CLIENT;
+		data = mockClientData;
+	    }
+	}, new Packet("Server hello") {
+	    {
+		type = SERVER;
+		data = mockServerData;
+	    }
+	} });
+
+	server.start();
+
+	// Connect to server and send and receive mock data
+
+	Socket socket = SocketFactory.getDefault().createSocket();
+	try {
+	    socket.connect(server.getAddress());
+
+	    InputStream is = socket.getInputStream();
+	    OutputStream os = socket.getOutputStream();
+
+	    // Write mock data to server
+	    os.write(mockClientData);
+
+	    // Read data from server
+	    byte actualData[] = new byte[mockServerData.length];
+	    int actualDataLength = is.read(actualData);
+
+	    // Compare mock data with actual data
+	    assertEquals("Unexpected length of actual data read from server.", mockServerData.length, actualDataLength);
+
+	    for (int i = 0; i < actualDataLength; i++) {
+		assertEquals("Unexpected byte #" + i + " in response", mockServerData[i], actualData[i]);
+	    }
+
+	    server.waitUntilShutdowned(1 * 1000 /* up to 1 second */);
+
+	    assertNull("Unexpected exception at mock server side.", server.getException());
+	    assertTrue("Server is not shutdowned at after conversation.", server.isShutdowned());
+
+	} finally {
+	    socket.close();
+	}
+    }
+
+    public void testIsMockServerCanUpgradeConnectionToSsl() throws Exception {
+
+	final byte[] mockClientData1 = new byte[] { 0x01, 0x02, 0x03 };
+	final byte[] mockServerData1 = new byte[] { 0x03, 0x02, 0x01 };
+
+	final byte[] mockClientData2 = new byte[] { 0x02, 0x04, 0x02, 0x03 };
+	final byte[] mockServerData2 = new byte[] { 0x02, 0x02, 0x01, 0x04 };
+
+	MockServer server = new MockServer(new Packet[] { new Packet("Client hello") {
+	    {
+		type = CLIENT;
+		data = mockClientData1;
+	    }
+	}, new Packet("Server hello") {
+	    {
+		type = SERVER;
+		data = mockServerData1;
+	    }
+	}, new Packet("Upgrade connection to SSL") {
+	    {
+		type = UPGRADE_TO_SSL;
+	    }
+	}, new Packet("Client data over SSL") {
+	    {
+		type = CLIENT;
+		data = mockClientData2;
+	    }
+	}, new Packet("Server data over SSL") {
+	    {
+		type = SERVER;
+		data = mockServerData2;
+	    }
+	} });
+
+	server.start();
+
+	// Connect to server and send and receive mock data
+
+	Socket socket = SocketFactory.getDefault().createSocket();
+	try {
+	    InetSocketAddress address = server.getAddress();
+	    socket.connect(address);
+
+	    // Send hello data over plain connection
+	    {
+		InputStream is = socket.getInputStream();
+		OutputStream os = socket.getOutputStream();
+
+		// Write mock data to server
+		os.write(mockClientData1);
+
+		// Read data from server
+		byte actualData[] = new byte[mockServerData1.length];
+		int actualDataLength = is.read(actualData);
+
+		// Compare mock data with actual data
+		assertEquals("Unexpected length of actual data read from server.", mockServerData1.length, actualDataLength);
+
+		for (int i = 0; i < actualDataLength; i++) {
+		    assertEquals("Unexpected byte #" + i + " in response", mockServerData1[i], actualData[i]);
+		}
+	    }
+
+	    // Upgrade connection to SSL and send mock data
+	    {
+		//System.setProperty("javax.net.debug", "ssl");
+
+		final SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+		SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, address.getHostString(), address.getPort(), true);
+		sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
+		sslSocket.startHandshake();
+
+		InputStream is = sslSocket.getInputStream();
+		OutputStream os = sslSocket.getOutputStream();
+
+		// Write mock data to server
+		os.write(mockClientData2);
+
+		// Read data from server
+		byte actualData[] = new byte[mockServerData2.length];
+		int actualDataLength = is.read(actualData);
+
+		// Compare mock data with actual data
+		assertEquals("Unexpected length of actual data read from server.", mockServerData2.length, actualDataLength);
+
+		for (int i = 0; i < actualDataLength; i++) {
+		    assertEquals("Unexpected byte #" + i + " in response", mockServerData2[i], actualData[i]);
+		}
+
+	    }
+
+	    server.waitUntilShutdowned(1 * 1000 /* up to 1 second */);
+
+	    assertNull("Unexpected exception at mock server side.", server.getException());
+	    assertTrue("Server is not shutdowned at after conversation.", server.isShutdowned());
+	} finally {
+	    socket.close();
+	}
+
+    }
+}


[6/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java
new file mode 100644
index 0000000..53e6d2f
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java
@@ -0,0 +1,1014 @@
+// 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.AssertingByteBuffer;
+import streamer.ByteBuffer;
+
+/**
+ * Based on code example from MSDN, @see
+ * http://msdn.microsoft.com/en-us/library/dd240593.aspx .
+ */
+public class RLEBitmapDecompression {
+
+  public static final int g_MaskRegularRunLength = 0x1F;
+  public static final int g_MaskLiteRunLength = 0x0F;
+
+  public static final int g_MaskSpecialFgBg1 = 0x03;
+  public static final int g_MaskSpecialFgBg2 = 0x05;
+
+  public static final int REGULAR_BG_RUN = 0x00;
+  public static final int REGULAR_FG_RUN = 0x01;
+  public static final int REGULAR_FGBG_IMAGE = 0x02;
+  public static final int REGULAR_COLOR_RUN = 0x03;
+  public static final int REGULAR_COLOR_IMAGE = 0x04;
+
+  public static final int LITE_SET_FG_FG_RUN = 0x0C;
+  public static final int LITE_SET_FG_FGBG_IMAGE = 0x0D;
+  public static final int LITE_DITHERED_RUN = 0x0E;
+
+  public static final int MEGA_MEGA_BG_RUN = 0xF0;
+  public static final int MEGA_MEGA_FG_RUN = 0xF1;
+  public static final int MEGA_MEGA_FGBG_IMAGE = 0xF2;
+  public static final int MEGA_MEGA_COLOR_RUN = 0xF3;
+  public static final int MEGA_MEGA_COLOR_IMAGE = 0xF4;
+  public static final int MEGA_MEGA_SET_FG_RUN = 0xF6;
+  public static final int MEGA_MEGA_SET_FGBG_IMAGE = 0xF7;
+  public static final int MEGA_MEGA_DITHERED_RUN = 0xF8;
+
+  public static final int SPECIAL_FGBG_1 = 0xF9;
+  public static final int SPECIAL_FGBG_2 = 0xFA;
+
+  public static final int SPECIAL_WHITE = 0xFD;
+  public static final int SPECIAL_BLACK = 0xFE;
+
+  /**
+   * Writes a pixel to the specified buffer and advance cursor by bpp.
+   * 
+   * @param bpp
+   *          bytes per pixel
+   */
+  private static void writePixel(int bpp, ByteBuffer destBuf, int pixel) {
+    switch (bpp) {
+    case 1:
+      destBuf.writeByte(pixel);
+      break;
+    case 2:
+      destBuf.writeShortLE(pixel);
+      break;
+    case 3:
+      destBuf.writeByte(pixel);
+      destBuf.writeShortLE(pixel >> 8);
+      break;
+    case 4:
+      destBuf.writeIntLE(pixel);
+      break;
+    default:
+      throw new RuntimeException("Unsupported color depth.");
+    }
+  }
+
+  /**
+   * Reads a pixel from the specified buffer at given offset without changing of
+   * cursor.
+   * 
+   * @param bpp
+   *          bytes per pixel
+   * @param offset
+   *          -rowDelta (i.e. (-width*bpp))
+   */
+  private static int peekPixel(int bpp, ByteBuffer destBuf, int offset) {
+    if (offset >= 0 || (-offset) > destBuf.cursor)
+      throw new RuntimeException("Incorrect value for offset: offset in destination buffer must point to pixel in previous row.");
+
+    // Adjust cursor to point to pixel in previous row
+    int oldCursor = destBuf.cursor;
+    destBuf.cursor += offset;
+
+    int pixel;
+    switch (bpp) {
+    case 1:
+      pixel = destBuf.readUnsignedByte();
+      break;
+    case 2:
+      pixel = destBuf.readUnsignedShortLE();
+      break;
+    case 3:
+      pixel = destBuf.readUnsignedByte() | (destBuf.readUnsignedShortLE() >> 8);
+      break;
+    case 4:
+      pixel = destBuf.readSignedIntLE();
+      break;
+    default:
+      throw new RuntimeException("Unsupported color depth.");
+    }
+    destBuf.cursor = oldCursor;
+
+    return pixel;
+  }
+
+  /**
+   * Reads a pixel from the specified buffer and advance cursor by bpp value.
+   * 
+   * @param bpp
+   *          bytes per pixel
+   */
+  private static int readPixel(int bpp, ByteBuffer srcBuf) {
+    int pixel;
+    switch (bpp) {
+    case 1:
+      pixel = srcBuf.readUnsignedByte();
+      break;
+    case 2:
+      pixel = srcBuf.readUnsignedShortLE();
+      break;
+    case 3:
+      pixel = srcBuf.readUnsignedByte() | (srcBuf.readUnsignedShortLE() >> 8);
+      break;
+    case 4:
+      pixel = srcBuf.readSignedIntLE();
+      break;
+    default:
+      throw new RuntimeException("Unsupported color depth.");
+    }
+
+    return pixel;
+  }
+
+  /**
+   * Returns the size of a pixel in bytes.
+   */
+  private static int getPixelSize(int colorDepth) {
+    switch (colorDepth) {
+    case 8:
+      return 1;
+    case 15:
+    case 16:
+      return 2;
+    case 24:
+      return 3;
+    default:
+      throw new RuntimeException("Unsupported pixel color depth: " + colorDepth + " bpp.");
+    }
+  }
+
+  /**
+   * Reads the supplied order header & extracts the compression order code ID.
+   */
+  private static int extractCodeId(int orderHeader) {
+    // Taken from FreeRDP code, bitmap.c
+    switch (orderHeader) {
+    case MEGA_MEGA_BG_RUN:
+    case MEGA_MEGA_FG_RUN:
+    case MEGA_MEGA_SET_FG_RUN:
+    case MEGA_MEGA_DITHERED_RUN:
+    case MEGA_MEGA_COLOR_RUN:
+    case MEGA_MEGA_FGBG_IMAGE:
+    case MEGA_MEGA_SET_FGBG_IMAGE:
+    case MEGA_MEGA_COLOR_IMAGE:
+    case SPECIAL_FGBG_1:
+    case SPECIAL_FGBG_2:
+    case SPECIAL_WHITE:
+    case SPECIAL_BLACK:
+      return orderHeader;
+    }
+
+    int code = orderHeader >> 5;
+    switch (code) {
+    case REGULAR_BG_RUN:
+    case REGULAR_FG_RUN:
+    case REGULAR_COLOR_RUN:
+    case REGULAR_FGBG_IMAGE:
+    case REGULAR_COLOR_IMAGE:
+      return code;
+    }
+
+    return orderHeader >> 4;
+  }
+
+  /**
+   * Returns a black pixel.
+   */
+  private static int getColorBlack() {
+    return 0x000000;
+  }
+
+  /**
+   * Returns a white pixel.
+   */
+  private static int getColorWhite(int colorDepth) {
+    if (colorDepth == 8) {
+      //
+      // Palette entry #255 holds white.
+      //
+      return 0xFF;
+    } else if (colorDepth == 15) {
+      //
+      // 5 bits per RGB component:
+      // 0111 1111 1111 1111 (binary)
+      //
+      return 0x7FFF;
+    } else if (colorDepth == 16) {
+      //
+      // 5 bits for red, 6 bits for green, 5 bits for green:
+      // 1111 1111 1111 1111 (binary)
+      //
+      return 0xFFFF;
+    } else if (colorDepth == 24) {
+      //
+      // 8 bits per RGB component:
+      // 1111 1111 1111 1111 1111 1111 (binary)
+      //
+      return 0xFFFFFF;
+    } else
+      throw new RuntimeException("Unsupported color depth.");
+  }
+
+  /**
+   * Extract the run length of a compression order.
+   */
+  private static int extractRunLength(int code, int orderHeader, ByteBuffer srcBuf) {
+    switch (code) {
+    case REGULAR_FGBG_IMAGE: {
+      int runLength = orderHeader & g_MaskRegularRunLength;
+      if (runLength == 0)
+        runLength = srcBuf.readUnsignedByte() + 1;
+      else
+        runLength = runLength * 8;
+      return runLength;
+    }
+    case LITE_SET_FG_FGBG_IMAGE: {
+      int runLength = orderHeader & g_MaskLiteRunLength;
+      if (runLength == 0)
+        runLength = srcBuf.readUnsignedByte() + 1;
+      else
+        runLength = runLength * 8;
+      return runLength;
+    }
+    case REGULAR_BG_RUN:
+    case REGULAR_COLOR_IMAGE:
+    case REGULAR_COLOR_RUN:
+    case REGULAR_FG_RUN: {
+      int runLength = orderHeader & g_MaskRegularRunLength;
+      if (runLength == 0)
+        // An extended (MEGA) run.
+        runLength = srcBuf.readUnsignedByte() + 32;
+      return runLength;
+    }
+    case LITE_DITHERED_RUN:
+    case LITE_SET_FG_FG_RUN: {
+      int runLength = orderHeader & g_MaskLiteRunLength;
+      if (runLength == 0)
+        // An extended (MEGA) run.
+        runLength = srcBuf.readUnsignedByte() + 16;
+      return runLength;
+    }
+    case MEGA_MEGA_BG_RUN:
+    case MEGA_MEGA_COLOR_IMAGE:
+    case MEGA_MEGA_COLOR_RUN:
+    case MEGA_MEGA_DITHERED_RUN:
+    case MEGA_MEGA_FG_RUN:
+    case MEGA_MEGA_FGBG_IMAGE:
+    case MEGA_MEGA_SET_FG_RUN:
+    case MEGA_MEGA_SET_FGBG_IMAGE: {
+      return srcBuf.readUnsignedShortLE();
+    }
+    default:
+      return 0;
+    }
+
+  }
+
+  /**
+   * Write a foreground/background image to a destination buffer.
+   */
+  private static void writeFgBgImage(int bpp, ByteBuffer destBuf, int rowDelta, int bitmask, int fgPel, int cBits) {
+    for (; cBits > 0; cBits--, bitmask >>= 1) {
+      int xorPixel = peekPixel(bpp, destBuf, -rowDelta);
+      writePixel(bpp, destBuf, ((bitmask & 0x1) > 0) ? xorPixel ^ fgPel : xorPixel);
+    }
+  }
+
+  /**
+   * Write a foreground/background image to a destination buffer for the first
+   * line of compressed data.
+   */
+  private static void writeFirstLineFgBgImage(int bpp, ByteBuffer destBuf, int bitmask, int fgPel, int cBits) {
+    for (; cBits > 0; cBits--, bitmask >>= 1) {
+      writePixel(bpp, destBuf, ((bitmask & 0x1) > 0) ? fgPel : getColorBlack());
+    }
+  }
+
+  /**
+   * Decompress a RLE compressed bitmap and flip decompressed image.
+   * 
+   * @param srcBuf
+   *          source buffer containing compressed bitmap
+   * @param imageWidth
+   *          width of destination image in pixels
+   * @param imageHeight
+   *          height of destination image in pixels
+   * @param colorDepth
+   *          bits per pixel
+   * @return destination image buffer
+   */
+  public static ByteBuffer rleDecompress(ByteBuffer srcBuf, int imageWidth, int imageHeight, int colorDepth) {
+    int bpp = getPixelSize(colorDepth);
+
+    // Decompress image
+    ByteBuffer destBuf = new ByteBuffer(new byte[imageWidth * imageHeight * bpp]);
+    rleDecompress(srcBuf, destBuf, imageWidth, imageHeight, colorDepth);
+
+    // Flip image
+    return flipRawImage(destBuf, imageWidth, imageHeight, bpp);
+  }
+
+  /**
+   * Decompress a RLE compressed bitmap.
+   * 
+   * @param srcBuf
+   *          source buffer containing compressed bitmap
+   * @param destBuf
+   *          destination buffer
+   * @param imageWidth
+   *          width of destination image in pixels
+   * @param imageHeight
+   *          height of destination image in pixels
+   * @param colorDepth
+   *          bits per pixel
+   */
+  protected static void rleDecompress(final ByteBuffer srcBuf, final ByteBuffer destBuf, final int imageWidth, final int imageHeight, final int colorDepth) {
+    final int bpp = getPixelSize(colorDepth);
+    final int rowDelta = imageWidth * bpp;
+    final int whitePixel = getColorWhite(colorDepth);
+    final int blackPixel = getColorBlack();
+
+    int fgPel = whitePixel;
+    boolean insertFgPel = false;
+    boolean firstLine = true;
+
+    if (destBuf.length != imageWidth * imageHeight * bpp)
+      throw new RuntimeException("Incorrect size of destination buffer. Expected size (imageWidth*imageHeight*bpp): " + (imageWidth * imageHeight * bpp)
+          + ", actual size: " + destBuf.length + ".");
+
+    while (srcBuf.cursor < srcBuf.length) {
+      // Watch out for the end of the first scanline in destination buffer.
+      if (firstLine) {
+        if (destBuf.cursor >= rowDelta) {
+          firstLine = false;
+          insertFgPel = false;
+        }
+      }
+
+      // Extract the compression order code ID from the compression
+      // order header.
+      int orderHeader = srcBuf.readUnsignedByte();
+      int code = extractCodeId(orderHeader);
+
+      // Handle Background Run Orders.
+      if (code == REGULAR_BG_RUN | code == MEGA_MEGA_BG_RUN) {
+        int runLength = extractRunLength(code, orderHeader, srcBuf);
+
+        if (firstLine) {
+          if (insertFgPel) {
+            writePixel(bpp, destBuf, fgPel);
+            runLength--;
+          }
+
+          for (; runLength > 0; runLength--)
+            writePixel(bpp, destBuf, blackPixel);
+
+        } else {
+          if (insertFgPel) {
+            writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta) ^ fgPel);
+            runLength--;
+          }
+
+          // Copy pixels from previous row of destination image
+          for (; runLength > 0; runLength--)
+            writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta));
+        }
+
+        //
+        // A follow-on background run order will need a
+        // foreground pel inserted.
+        //
+        insertFgPel = true;
+        continue;
+      }
+
+      //
+      // For any of the other run-types a follow-on background run
+      // order does not need a foreground pel inserted.
+      //
+      insertFgPel = false;
+
+      //
+      // Handle Foreground Run Orders.
+      //
+      if (code == REGULAR_FG_RUN | code == MEGA_MEGA_FG_RUN | code == LITE_SET_FG_FG_RUN | code == MEGA_MEGA_SET_FG_RUN) {
+        int runLength = extractRunLength(code, orderHeader, srcBuf);
+
+        if (code == LITE_SET_FG_FG_RUN | code == MEGA_MEGA_SET_FG_RUN)
+          fgPel = readPixel(bpp, srcBuf);
+
+        if (firstLine) {
+          for (; runLength > 0; runLength--) {
+            writePixel(bpp, destBuf, fgPel);
+          }
+        } else {
+          for (; runLength > 0; runLength--) {
+            writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta) ^ fgPel);
+          }
+        }
+
+        continue;
+      }
+
+      //
+      // Handle Dithered Run Orders.
+      //
+      if (code == LITE_DITHERED_RUN | code == MEGA_MEGA_DITHERED_RUN) {
+        int runLength = extractRunLength(code, orderHeader, srcBuf);
+
+        int pixelA = readPixel(bpp, srcBuf);
+        int pixelB = readPixel(bpp, srcBuf);
+
+        for (; runLength > 0; runLength--) {
+          writePixel(bpp, destBuf, pixelA);
+          writePixel(bpp, destBuf, pixelB);
+        }
+
+        continue;
+      }
+
+      //
+      // Handle Color Run Orders.
+      //
+      if (code == REGULAR_COLOR_RUN | code == MEGA_MEGA_COLOR_RUN) {
+        int runLength = extractRunLength(code, orderHeader, srcBuf);
+
+        int pixelA = readPixel(bpp, srcBuf);
+
+        for (; runLength > 0; runLength--)
+          writePixel(bpp, destBuf, pixelA);
+
+        continue;
+      }
+
+      //
+      // Handle Foreground/Background Image Orders.
+      //
+      if (code == REGULAR_FGBG_IMAGE | code == MEGA_MEGA_FGBG_IMAGE | code == LITE_SET_FG_FGBG_IMAGE | code == MEGA_MEGA_SET_FGBG_IMAGE) {
+        int runLength = extractRunLength(code, orderHeader, srcBuf);
+
+        if (code == LITE_SET_FG_FGBG_IMAGE | code == MEGA_MEGA_SET_FGBG_IMAGE) {
+          fgPel = readPixel(bpp, srcBuf);
+        }
+
+        for (; runLength > 8; runLength -= 8) {
+          int bitmask = srcBuf.readUnsignedByte();
+
+          if (firstLine)
+            writeFirstLineFgBgImage(bpp, destBuf, bitmask, fgPel, 8);
+          else
+            writeFgBgImage(bpp, destBuf, rowDelta, bitmask, fgPel, 8);
+        }
+
+        if (runLength > 0) {
+          int bitmask = srcBuf.readUnsignedByte();
+
+          if (firstLine)
+            writeFirstLineFgBgImage(bpp, destBuf, bitmask, fgPel, runLength);
+          else
+            writeFgBgImage(bpp, destBuf, rowDelta, bitmask, fgPel, runLength);
+        }
+
+        continue;
+      }
+
+      //
+      // Handle Color Image Orders.
+      //
+      if (code == REGULAR_COLOR_IMAGE | code == MEGA_MEGA_COLOR_IMAGE) {
+        int runLength = extractRunLength(code, orderHeader, srcBuf);
+
+        /* DEBUG */
+        // Copy bytes from source to destination using writeByte(readByte())
+        // for (int byteCount = runLength * bpp; byteCount > 0; byteCount--) {
+        // destBuf.writeByte(srcBuf.readUnsignedByte());
+        // }
+        /* DEBUG */
+
+        // Copy bytes from source to destination directly using arraycopy()
+        int lengthInBytes = runLength * bpp;
+        System.arraycopy(srcBuf.data, srcBuf.offset + srcBuf.cursor, destBuf.data, destBuf.offset + destBuf.cursor, lengthInBytes);
+        srcBuf.cursor += lengthInBytes;
+        destBuf.cursor += lengthInBytes;
+
+        continue;
+      }
+
+      //
+      // Handle Special Order 1.
+      //
+      if (code == SPECIAL_FGBG_1) {
+        if (firstLine)
+          writeFirstLineFgBgImage(bpp, destBuf, g_MaskSpecialFgBg1, fgPel, 8);
+        else
+          writeFgBgImage(bpp, destBuf, rowDelta, g_MaskSpecialFgBg1, fgPel, 8);
+
+        continue;
+      }
+
+      //
+      // Handle Special Order 2.
+      //
+      if (code == SPECIAL_FGBG_2) {
+        if (firstLine)
+          writeFirstLineFgBgImage(bpp, destBuf, g_MaskSpecialFgBg2, fgPel, 8);
+        else
+          writeFgBgImage(bpp, destBuf, rowDelta, g_MaskSpecialFgBg2, fgPel, 8);
+
+        continue;
+      }
+
+      //
+      // Handle White Order.
+      //
+      if (code == SPECIAL_WHITE) {
+        writePixel(bpp, destBuf, whitePixel);
+
+        continue;
+      }
+
+      //
+      // Handle Black Order.
+      //
+      if (code == SPECIAL_BLACK) {
+        writePixel(bpp, destBuf, blackPixel);
+
+        continue;
+      }
+
+      throw new RuntimeException("Unknown code: " + code + ".");
+    }
+  }
+
+  /**
+   * Flip image in vertical direction.
+   */
+  public static ByteBuffer flipRawImage(ByteBuffer src, int width, int height, int bpp) {
+    if (width * height * bpp != src.length)
+      throw new RuntimeException("Incorrect size of buffer. Expected size (imageWidth*imageHeight*bpp): " + (width * height * bpp) + ", actual size: "
+          + src.length + ".");
+    ByteBuffer dest = new ByteBuffer(new byte[src.length]);
+
+    int scanLine = width * bpp;
+
+    for (int i = 0; i < height; i++) {
+      // Copy one row
+      System.arraycopy(src.data, (height - i - 1) * scanLine, dest.data, i * scanLine, scanLine);
+    }
+
+    return dest;
+
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+
+    if (true) {
+      // 16x1@8bpp, all black
+      int width = 16, height = 1, depth = 8, bpp = depth / 8;
+      ByteBuffer src = new ByteBuffer(new byte[] { 0x10 });
+      ByteBuffer dest = new AssertingByteBuffer(new byte[width * height * bpp]);
+      rleDecompress(src, dest, width, height, depth);
+    }
+
+    if (true) {
+      // 16x1@16bpp, all black
+      int width = 16, height = 1, depth = 16, bpp = depth / 8;
+      ByteBuffer src = new ByteBuffer(new byte[] { 0x0c, (byte) 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+      ByteBuffer dest = new AssertingByteBuffer(new byte[width * height * bpp]);
+      rleDecompress(src, dest, width, height, depth);
+    }
+
+    if (true) {
+      // 32x32@8
+      int width = 32, height = 32, depth = 8, bpp = depth / 8;
+
+      ByteBuffer src = new ByteBuffer(new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, (byte) 0x06, (byte) 0x06,
+          (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xec, (byte) 0x6c, (byte) 0x0e,
+          (byte) 0x0e, (byte) 0x44, (byte) 0x0e, (byte) 0x0e, (byte) 0x0e, (byte) 0x13, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06,
+          (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0xe4, (byte) 0x04, (byte) 0x06,
+          (byte) 0x8e, (byte) 0x60, (byte) 0x0e, (byte) 0x60, (byte) 0x8c, (byte) 0xb4, (byte) 0xb5, (byte) 0xdc, (byte) 0xdc, (byte) 0xbb, (byte) 0xb4,
+          (byte) 0x8c, (byte) 0x66, (byte) 0x0b, (byte) 0x6c, (byte) 0xe4, (byte) 0x04, (byte) 0x06, (byte) 0x02, (byte) 0x8b, (byte) 0x06, (byte) 0x06,
+          (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xf8, (byte) 0x0e, (byte) 0x66, (byte) 0xb4, (byte) 0xdc, (byte) 0x68, (byte) 0xe2,
+          (byte) 0x97, (byte) 0xdd, (byte) 0xb4, (byte) 0xa7, (byte) 0x16, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed,
+          (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x0b, (byte) 0xae,
+          (byte) 0xdc, (byte) 0xe9, (byte) 0x6a, (byte) 0xdc, (byte) 0x96, (byte) 0xe9, (byte) 0xe9, (byte) 0xb4, (byte) 0x0e, (byte) 0x00, (byte) 0x06,
+          (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06,
+          (byte) 0x0e, (byte) 0xae, (byte) 0xdc, (byte) 0xdb, (byte) 0xdb, (byte) 0xd0, (byte) 0x09, (byte) 0x07, (byte) 0xcf, (byte) 0x03, (byte) 0x95,
+          (byte) 0xdb, (byte) 0xdb, (byte) 0xdc, (byte) 0xb4, (byte) 0x66, (byte) 0x6c, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0x00,
+          (byte) 0x00, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x0b, (byte) 0xae, (byte) 0xdb, (byte) 0xd4, (byte) 0xd5, (byte) 0x6c,
+          (byte) 0xdb, (byte) 0x80, (byte) 0xaf, (byte) 0xd5, (byte) 0xd4, (byte) 0xdb, (byte) 0xb4, (byte) 0x66, (byte) 0x04, (byte) 0x06, (byte) 0x04,
+          (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x66, (byte) 0xae, (byte) 0xd5, (byte) 0xad, (byte) 0xd4,
+          (byte) 0xd4, (byte) 0xd5, (byte) 0xd5, (byte) 0xd5, (byte) 0xdb, (byte) 0xb4, (byte) 0xb4, (byte) 0xb4, (byte) 0xb4, (byte) 0xb4, (byte) 0xd5,
+          (byte) 0xd5, (byte) 0xd5, (byte) 0xd4, (byte) 0xd4, (byte) 0xad, (byte) 0xd5, (byte) 0xb4, (byte) 0x0e, (byte) 0x06, (byte) 0x06, (byte) 0x06,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x60, (byte) 0xa7, (byte) 0xb4, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xb3,
+          (byte) 0xb3, (byte) 0xd4, (byte) 0xd4, (byte) 0xb3, (byte) 0x8c, (byte) 0xb6, (byte) 0x07, (byte) 0xb6, (byte) 0x8c, (byte) 0xb3, (byte) 0xd4,
+          (byte) 0xb3, (byte) 0xb3, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xb4, (byte) 0xad, (byte) 0x66, (byte) 0x00, (byte) 0x06, (byte) 0x00,
+          (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0x66, (byte) 0xae, (byte) 0xad, (byte) 0x8b, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad,
+          (byte) 0xad, (byte) 0xb3, (byte) 0xad, (byte) 0xb5, (byte) 0x07, (byte) 0x07, (byte) 0x07, (byte) 0xf0, (byte) 0x8b, (byte) 0xad, (byte) 0xad,
+          (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0x8b, (byte) 0xa7, (byte) 0xae, (byte) 0xa7, (byte) 0x6c, (byte) 0x06, (byte) 0x00, (byte) 0x00,
+          (byte) 0x04, (byte) 0x6c, (byte) 0xa7, (byte) 0xad, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad,
+          (byte) 0xad, (byte) 0xad, (byte) 0xb5, (byte) 0xbd, (byte) 0xbd, (byte) 0xbd, (byte) 0xbd, (byte) 0xf0, (byte) 0x8b, (byte) 0x8b, (byte) 0xad,
+          (byte) 0x8b, (byte) 0x8b, (byte) 0xa7, (byte) 0xa7, (byte) 0xc8, (byte) 0xc8, (byte) 0x60, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+          (byte) 0x66, (byte) 0xc8, (byte) 0xa7, (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b, (byte) 0x8b, (byte) 0x8b, (byte) 0x8b, (byte) 0xad,
+          (byte) 0x8b, (byte) 0x92, (byte) 0xf1, (byte) 0xf1, (byte) 0xf1, (byte) 0xf1, (byte) 0xf2, (byte) 0x07, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b,
+          (byte) 0xa7, (byte) 0xa7, (byte) 0x66, (byte) 0x66, (byte) 0xc8, (byte) 0x66, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x60,
+          (byte) 0xa7, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b, (byte) 0x8b, (byte) 0x8b,
+          (byte) 0xa7, (byte) 0xb6, (byte) 0xf3, (byte) 0xf3, (byte) 0xf3, (byte) 0xf3, (byte) 0xf3, (byte) 0x07, (byte) 0x66, (byte) 0xa7, (byte) 0xa7,
+          (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x6c, (byte) 0x04, (byte) 0xa7,
+          (byte) 0x60, (byte) 0x6b, (byte) 0x66, (byte) 0x99, (byte) 0xb6, (byte) 0xf5, (byte) 0xf5, (byte) 0xf5, (byte) 0xf5, (byte) 0xf5, (byte) 0xef,
+          (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0x66, (byte) 0x00, (byte) 0x00, (byte) 0x60,
+          (byte) 0xa7, (byte) 0x66, (byte) 0x60, (byte) 0x66, (byte) 0x66, (byte) 0x8c, (byte) 0xf1, (byte) 0x6e, (byte) 0xff, (byte) 0x85, (byte) 0xbd,
+          (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x60, (byte) 0x05, (byte) 0x87, (byte) 0x13, (byte) 0x04, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+          (byte) 0x66, (byte) 0xf4, (byte) 0x70, (byte) 0xff, (byte) 0x84, (byte) 0xbd, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x05, (byte) 0x85,
+          (byte) 0x0b, (byte) 0xa7, (byte) 0xb5, (byte) 0xae, (byte) 0x8c, (byte) 0xd0, (byte) 0x13, (byte) 0xc1, (byte) 0x01, (byte) 0x00, (byte) 0x08,
+          (byte) 0x8e, (byte) 0x8c, (byte) 0xae, (byte) 0xb5, (byte) 0xae, (byte) 0x66, (byte) 0x00, (byte) 0x00, (byte) 0x6c, (byte) 0xae, (byte) 0xbc,
+          (byte) 0xb5, (byte) 0xb5, (byte) 0xae, (byte) 0xb5, (byte) 0xd0, (byte) 0x0e, (byte) 0x0c, (byte) 0x01, (byte) 0x00, (byte) 0x90, (byte) 0xf2,
+          (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xbc, (byte) 0xb5, (byte) 0x66, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0xae,
+          (byte) 0x0a, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0x68, (byte) 0xae, (byte) 0x82, (byte) 0x8c, (byte) 0x0a, (byte) 0x05, (byte) 0x8c,
+          (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xbc, (byte) 0xb5, (byte) 0x6c, (byte) 0x00, (byte) 0x00,
+          (byte) 0x06, (byte) 0x05, (byte) 0x81, (byte) 0xd0, (byte) 0x06, (byte) 0x9a, (byte) 0x8c, (byte) 0x0a, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0x0a, (byte) 0xb5,
+          (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x8b, (byte) 0x0a, (byte) 0xbc, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0x06,
+          (byte) 0x9b, (byte) 0xb6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xae,
+          (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0x0a, (byte) 0x8c, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x6c,
+          (byte) 0xb5, (byte) 0x0a, (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0x05, (byte) 0x80, (byte) 0x7d, (byte) 0xbc, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6,
+          (byte) 0x0a, (byte) 0x0a, (byte) 0x8b, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x06, (byte) 0x87, (byte) 0x0a, (byte) 0xbc,
+          (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xd0, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xb6, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xf2, (byte) 0xd0, (byte) 0xae, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xbc, (byte) 0x1a,
+          (byte) 0xb5, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0xed, (byte) 0x06, (byte) 0x6e, (byte) 0xb5, (byte) 0x0a, (byte) 0xbc,
+          (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xd0, (byte) 0xd0, (byte) 0xd0, (byte) 0xb5, (byte) 0xf4, (byte) 0xff, (byte) 0xf2,
+          (byte) 0xd0, (byte) 0xd0, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xbc, (byte) 0x0a, (byte) 0x0a, (byte) 0x8b,
+          (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x8b, (byte) 0xbc, (byte) 0x1a, (byte) 0x0a,
+          (byte) 0xb6, (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5,
+          (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xb6, (byte) 0x0a, (byte) 0xde, (byte) 0x0a, (byte) 0xa7, (byte) 0x06, (byte) 0x00,
+          (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x8b, (byte) 0xbc, (byte) 0xf2, (byte) 0x0a,
+          (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6,
+          (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0x0a, (byte) 0xf2, (byte) 0x1a, (byte) 0x8c, (byte) 0xec, (byte) 0x06, (byte) 0x06, (byte) 0x06,
+          (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0xa7, (byte) 0xbc, (byte) 0x1a, (byte) 0x0a,
+          (byte) 0x0a, (byte) 0x6a, (byte) 0xb6, (byte) 0x96, (byte) 0x0a, (byte) 0x0a, (byte) 0xf2, (byte) 0x0a, (byte) 0x87, (byte) 0x06, (byte) 0x04,
+          (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06,
+          (byte) 0x8c, (byte) 0xb6, (byte) 0xf4, (byte) 0xf2, (byte) 0xd0, (byte) 0x09, (byte) 0xbc, (byte) 0x87, (byte) 0x03, (byte) 0x80, (byte) 0x2c,
+          (byte) 0xde, (byte) 0xf4, (byte) 0x0a, (byte) 0x8b, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x6c, (byte) 0x87, (byte) 0x0a,
+          (byte) 0xf4, (byte) 0xf4, (byte) 0xf2, (byte) 0xde, (byte) 0xbd, (byte) 0xbd, (byte) 0xde, (byte) 0xf2, (byte) 0xf4, (byte) 0xf4, (byte) 0x0a,
+          (byte) 0xd0, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00,
+          (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x6c, (byte) 0x8c,
+          (byte) 0xb5, (byte) 0xbc, (byte) 0x0a, (byte) 0xde, (byte) 0xf2, (byte) 0xbd, (byte) 0x0a, (byte) 0xb5, (byte) 0x8c, (byte) 0x6c, (byte) 0x06,
+          (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0xe6,
+          (byte) 0x04, (byte) 0x06, (byte) 0x86, (byte) 0x04, (byte) 0x6c, (byte) 0x04, (byte) 0x8b, (byte) 0x04, (byte) 0x6c, (byte) 0xe6, (byte) 0x04,
+          (byte) 0x06, (byte) 0x82, (byte) 0x00, (byte) 0x00
+
+      });
+
+      ByteBuffer flippedImage = new ByteBuffer(new byte[] { (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04,
+          (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x6c, (byte) 0x04, (byte) 0x8b, (byte) 0x04, (byte) 0x6c,
+          (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04,
+          (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06,
+          (byte) 0x06, (byte) 0x6c, (byte) 0x8c, (byte) 0xb5, (byte) 0xbc, (byte) 0x0a, (byte) 0xde, (byte) 0xf2, (byte) 0xbd, (byte) 0x0a, (byte) 0xb5,
+          (byte) 0x8c, (byte) 0x6c, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x6c, (byte) 0x87,
+          (byte) 0x0a, (byte) 0xf4, (byte) 0xf4, (byte) 0xf2, (byte) 0xde, (byte) 0xbd, (byte) 0xbd, (byte) 0xde, (byte) 0xf2, (byte) 0xf4, (byte) 0xf4,
+          (byte) 0x0a, (byte) 0xd0, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x00,
+          (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x8c, (byte) 0xb6, (byte) 0xf4, (byte) 0xf2,
+          (byte) 0x0a, (byte) 0x0a, (byte) 0x0a, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0x0a, (byte) 0x0a, (byte) 0x0a, (byte) 0xde,
+          (byte) 0xf4, (byte) 0x0a, (byte) 0x8b, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x00, (byte) 0x00,
+          (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0xa7, (byte) 0xbc, (byte) 0x1a, (byte) 0x0a, (byte) 0x0a, (byte) 0xb6,
+          (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0x0a, (byte) 0x0a,
+          (byte) 0xf2, (byte) 0x0a, (byte) 0x87, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+          (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x8b, (byte) 0xbc, (byte) 0xf2, (byte) 0x0a, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6,
+          (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0xb6, (byte) 0x0a,
+          (byte) 0xf2, (byte) 0x1a, (byte) 0x8c, (byte) 0xec, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+          (byte) 0x04, (byte) 0x8b, (byte) 0xbc, (byte) 0x1a, (byte) 0x0a, (byte) 0xb6, (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5,
+          (byte) 0xb5, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xb6, (byte) 0x0a,
+          (byte) 0xde, (byte) 0x0a, (byte) 0xa7, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0xed, (byte) 0x06, (byte) 0x6e,
+          (byte) 0xb5, (byte) 0x0a, (byte) 0xbc, (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xd0, (byte) 0xd0, (byte) 0xd0, (byte) 0xb5,
+          (byte) 0xf4, (byte) 0xff, (byte) 0xf2, (byte) 0xd0, (byte) 0xd0, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xbc,
+          (byte) 0x0a, (byte) 0x0a, (byte) 0x8b, (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x06, (byte) 0x87, (byte) 0x0a,
+          (byte) 0xbc, (byte) 0xb6, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xd0, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xb6, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xd0, (byte) 0xae, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0xbc,
+          (byte) 0x1a, (byte) 0xb5, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x6c, (byte) 0xb5, (byte) 0x0a, (byte) 0xb6,
+          (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xbc, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0x0a,
+          (byte) 0x0a, (byte) 0x8b, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x8b, (byte) 0x0a, (byte) 0xbc, (byte) 0xb5, (byte) 0xb5,
+          (byte) 0xb5, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xb6, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xb6, (byte) 0x0a,
+          (byte) 0x8c, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0xae, (byte) 0x0a, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xd0,
+          (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0x8c, (byte) 0x0a, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xd0, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0x0a, (byte) 0xb5,
+          (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0xae, (byte) 0x0a, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xae, (byte) 0xae,
+          (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0xae, (byte) 0x8c, (byte) 0x0a, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xb5, (byte) 0xbc, (byte) 0xb5, (byte) 0x6c,
+          (byte) 0x00, (byte) 0x00, (byte) 0x6c, (byte) 0xae, (byte) 0xbc, (byte) 0xb5, (byte) 0xb5, (byte) 0xae, (byte) 0xb5, (byte) 0xf3, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xf2, (byte) 0xae, (byte) 0xae, (byte) 0xb5, (byte) 0xb5, (byte) 0xbc, (byte) 0xb5, (byte) 0x66, (byte) 0x00,
+          (byte) 0x00, (byte) 0x0b, (byte) 0xa7, (byte) 0xb5, (byte) 0xae, (byte) 0x8c, (byte) 0xa7, (byte) 0xf4, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xbd, (byte) 0xa7, (byte) 0x8c, (byte) 0xae, (byte) 0xb5, (byte) 0xae, (byte) 0x66, (byte) 0x00, (byte) 0x00,
+          (byte) 0x13, (byte) 0x04, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xf4, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xbd, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0x66, (byte) 0x00, (byte) 0x00, (byte) 0x60,
+          (byte) 0xa7, (byte) 0x66, (byte) 0x60, (byte) 0x66, (byte) 0x66, (byte) 0x8c, (byte) 0xf1, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xbd,
+          (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x60, (byte) 0x66, (byte) 0xa7, (byte) 0x66, (byte) 0x00, (byte) 0x00, (byte) 0x6c, (byte) 0x04,
+          (byte) 0xa7, (byte) 0x60, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66,
+          (byte) 0x66, (byte) 0x66, (byte) 0xb6, (byte) 0xf5, (byte) 0xf5, (byte) 0xf5, (byte) 0xf5, (byte) 0xf5, (byte) 0xef, (byte) 0x66, (byte) 0x66,
+          (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0x66, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0xa7,
+          (byte) 0x66, (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b, (byte) 0x8b, (byte) 0x8b, (byte) 0xa7,
+          (byte) 0xb6, (byte) 0xf3, (byte) 0xf3, (byte) 0xf3, (byte) 0xf3, (byte) 0xf3, (byte) 0x07, (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0x66,
+          (byte) 0x66, (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x66, (byte) 0xc8, (byte) 0xa7,
+          (byte) 0x66, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b, (byte) 0x8b, (byte) 0x8b, (byte) 0x8b, (byte) 0xad, (byte) 0x8b, (byte) 0x92, (byte) 0xf1,
+          (byte) 0xf1, (byte) 0xf1, (byte) 0xf1, (byte) 0xf2, (byte) 0x07, (byte) 0xa7, (byte) 0xa7, (byte) 0x8b, (byte) 0xa7, (byte) 0xa7, (byte) 0x66,
+          (byte) 0x66, (byte) 0xc8, (byte) 0x66, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x6c, (byte) 0xa7, (byte) 0xad, (byte) 0xa7,
+          (byte) 0xa7, (byte) 0x8b, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xb5, (byte) 0xbd, (byte) 0xbd,
+          (byte) 0xbd, (byte) 0xbd, (byte) 0xf0, (byte) 0x8b, (byte) 0x8b, (byte) 0xad, (byte) 0x8b, (byte) 0x8b, (byte) 0xa7, (byte) 0xa7, (byte) 0xc8,
+          (byte) 0xc8, (byte) 0x60, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0x66, (byte) 0xae, (byte) 0xad, (byte) 0x8b,
+          (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xb3, (byte) 0xad, (byte) 0xb5, (byte) 0x07, (byte) 0x07, (byte) 0x07,
+          (byte) 0xf0, (byte) 0x8b, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0x8b, (byte) 0xa7, (byte) 0xae, (byte) 0xa7,
+          (byte) 0x6c, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x60, (byte) 0xa7, (byte) 0xb4, (byte) 0xad, (byte) 0xad,
+          (byte) 0xad, (byte) 0xb3, (byte) 0xb3, (byte) 0xd4, (byte) 0xd4, (byte) 0xb3, (byte) 0x8c, (byte) 0xb6, (byte) 0x07, (byte) 0xb6, (byte) 0x8c,
+          (byte) 0xb3, (byte) 0xd4, (byte) 0xb3, (byte) 0xb3, (byte) 0xad, (byte) 0xad, (byte) 0xad, (byte) 0xb4, (byte) 0xad, (byte) 0x66, (byte) 0x00,
+          (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x66, (byte) 0xae, (byte) 0xd5, (byte) 0xad, (byte) 0xd4,
+          (byte) 0xd4, (byte) 0xd5, (byte) 0xd5, (byte) 0xd5, (byte) 0xdb, (byte) 0xb4, (byte) 0xb4, (byte) 0xb4, (byte) 0xb4, (byte) 0xb4, (byte) 0xd5,
+          (byte) 0xd5, (byte) 0xd5, (byte) 0xd4, (byte) 0xd4, (byte) 0xad, (byte) 0xd5, (byte) 0xb4, (byte) 0x0e, (byte) 0x06, (byte) 0x06, (byte) 0x06,
+          (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x0b, (byte) 0xae, (byte) 0xdb, (byte) 0xd4, (byte) 0xd5,
+          (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb,
+          (byte) 0xdb, (byte) 0xd5, (byte) 0xd4, (byte) 0xdb, (byte) 0xb4, (byte) 0x66, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00,
+          (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x0e, (byte) 0xae, (byte) 0xdc, (byte) 0xdb, (byte) 0xdb,
+          (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdc, (byte) 0xdc, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb, (byte) 0xdb,
+          (byte) 0xdb, (byte) 0xdc, (byte) 0xb4, (byte) 0x66, (byte) 0x6c, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x0b, (byte) 0xae, (byte) 0xdc, (byte) 0xe9, (byte) 0xdc,
+          (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xe9, (byte) 0xe9,
+          (byte) 0xb4, (byte) 0x0e, (byte) 0x00, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06,
+          (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xf8, (byte) 0x0e, (byte) 0x66, (byte) 0xb4, (byte) 0xdc, (byte) 0xe2,
+          (byte) 0xe2, (byte) 0xe2, (byte) 0xe2, (byte) 0xe2, (byte) 0xe2, (byte) 0xe2, (byte) 0xe2, (byte) 0xdd, (byte) 0xb4, (byte) 0xa7, (byte) 0x16,
+          (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x06,
+          (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x60, (byte) 0x0e, (byte) 0x60, (byte) 0x8c, (byte) 0xb4,
+          (byte) 0xb5, (byte) 0xdc, (byte) 0xdc, (byte) 0xbb, (byte) 0xb4, (byte) 0x8c, (byte) 0x66, (byte) 0x0b, (byte) 0x6c, (byte) 0x04, (byte) 0x06,
+          (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x04, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x06, (byte) 0xed,
+          (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xec, (byte) 0x6c, (byte) 0x0e, (byte) 0x0e,
+          (byte) 0x44, (byte) 0x0e, (byte) 0x0e, (byte) 0x0e, (byte) 0x13, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06,
+          (byte) 0x06, (byte) 0xed, (byte) 0x06, (byte) 0x06, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
+      ByteBuffer dest = new AssertingByteBuffer(flipRawImage(flippedImage, width, height, bpp).data);
+
+      rleDecompress(src, dest, width, height, depth);
+
+    }
+
+    if (true) {
+      // 32x32@16
+      int width = 32, height = 32, depth = 16;
+
+      ByteBuffer src = new ByteBuffer(new byte[] { (byte) 0x85, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x06, (byte) 0x8b, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x06, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0x16, (byte) 0x69, (byte) 0x99, (byte) 0xd6, (byte) 0x06, (byte) 0x69, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x04, (byte) 0xcc, (byte) 0x89, (byte) 0x52, (byte) 0x03, (byte) 0x6e, (byte) 0xff, (byte) 0xff, (byte) 0x02, (byte) 0x6e,
+          (byte) 0x08, (byte) 0x42, (byte) 0x01, (byte) 0x70, (byte) 0x08, (byte) 0x42, (byte) 0x71, (byte) 0xff, (byte) 0xff, (byte) 0xce, (byte) 0x18,
+          (byte) 0xc6, (byte) 0x01, (byte) 0x81, (byte) 0x08, (byte) 0x42, (byte) 0xce, (byte) 0x66, (byte) 0x29, (byte) 0x02, (byte) 0xcd, (byte) 0x89,
+          (byte) 0x52, (byte) 0x03, (byte) 0x88, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xd8, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x03, (byte) 0xf8, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x66, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x05, (byte) 0x6a, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0xc4, (byte) 0xcc, (byte) 0x89, (byte) 0x52, (byte) 0x03, (byte) 0x6e,
+          (byte) 0xff, (byte) 0xff, (byte) 0x02, (byte) 0x6e, (byte) 0x08, (byte) 0x42, (byte) 0x01, (byte) 0x70, (byte) 0x08, (byte) 0x42, (byte) 0x71,
+          (byte) 0xff, (byte) 0xff, (byte) 0xce, (byte) 0x18, (byte) 0xc6, (byte) 0x01, (byte) 0x81, (byte) 0x08, (byte) 0x42, (byte) 0xce, (byte) 0x66,
+          (byte) 0x29, (byte) 0x02, (byte) 0xcd, (byte) 0x89, (byte) 0x52, (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0xc3, (byte) 0x80, (byte) 0x61, (byte) 0x00, (byte) 0xa5, (byte) 0x80, (byte) 0x40, (byte) 0xec, (byte) 0x52, (byte) 0x00, (byte) 0x5a,
+          (byte) 0x00, (byte) 0x2d, (byte) 0x00, (byte) 0x24, (byte) 0x00, (byte) 0x12, (byte) 0x00, (byte) 0x24, (byte) 0x00, (byte) 0x12, (byte) 0x00,
+          (byte) 0x5a, (byte) 0x00, (byte) 0x2d, (byte) 0x00, (byte) 0xa5, (byte) 0x80, (byte) 0x52, (byte) 0x00, (byte) 0xc3, (byte) 0x80, (byte) 0x61,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xcc, (byte) 0x89, (byte) 0x52, (byte) 0x03, (byte) 0x6e, (byte) 0xff,
+          (byte) 0xff, (byte) 0x02, (byte) 0xcb, (byte) 0x18, (byte) 0xc6, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0xff, (byte) 0xff, });
+
+      ByteBuffer dest = new AssertingByteBuffer(new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10,
+          (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84,
+          (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10,
+          (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0xff, (byte) 0xff, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84,
+          (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10,
+          (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84,
+          (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10,
+          (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10,
+          (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10,
+          (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84,
+          (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x99,
+          (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0xff,
+          (byte) 0xff, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84,
+          (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10,
+          (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10,
+          (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10,
+          (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6,
+          (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99,
+          (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x99, (byte) 0xd6, (byte) 0x10, (byte) 0x84, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10,
+          (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84,
+          (byte) 0x10, (byte) 0x84, (byte) 0x10, (byte) 0x84, (byte) 0x99, (byte) 0xd6, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+          (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08,
+          (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42,
+          (byte) 0x08, (byte) 0x42, (byte) 0x08, (byte) 0x42, (byte) 0xff, (byte) 0xff, });
+
+      rleDecompress(src, dest, width, height, depth);
+
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java
new file mode 100644
index 0000000..cc8d1f1
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java
@@ -0,0 +1,214 @@
+// 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.PipelineImpl;
+import streamer.Queue;
+
+import common.AwtCanvasAdapter;
+import common.AwtKeyEventSource;
+import common.AwtMouseEventSource;
+import common.BufferedImageCanvas;
+import common.ScreenDescription;
+
+public class RdpClient extends PipelineImpl {
+
+  /**
+   * Name of last OneTimePacket in handshake sequence.
+   */
+  private static final String HANDSHAKE_END = "server_valid_client";
+
+  public RdpClient(String id, String userName, ScreenDescription screen, BufferedImageCanvas canvas) {
+    super(id);
+    assembleRDPPipeline(userName, screen, canvas);
+  }
+
+//  /* DEBUG */
+//  @Override
+//  protected HashMap<String, streamer.Element> initElementMap(String id) {
+//    HashMap<String, streamer.Element> map = new HashMap<String, streamer.Element>();
+//    map.put("IN", new ServerPacketSniffer("server <"));
+//    map.put("OUT", new ClientPacketSniffer("> client"));
+//    return map;
+//  }
+
+  private void assembleRDPPipeline(String userName, ScreenDescription screen, BufferedImageCanvas canvas) {
+    //
+    // Handshake chain
+    //
+
+    RdpState state = new RdpState();
+    int[] channelsToJoin = new int[] { RdpConstants.CHANNEL_RDPRDR, RdpConstants.CHANNEL_IO };
+
+    // Add elements
+
+    add(
+
+    new ClientX224ConnectionRequestPDU("client_connection_req", userName), new ServerX224ConnectionConfirmPDU("server_connection_conf"),
+
+    new UpgradeSocketToSSL("upgrade_to_ssl"),
+
+    new ClientMCSConnectInitial("client_initial_conference_create"), new ServerMCSConnectResponse("server_initial_conference_create"),
+
+    new ClientMCSErectDomainRequest("client_erect_domain"),
+
+    new ClientMCSAttachUserRequest("client_atach_user"), new ServerMCSAttachUserConfirmPDU("server_atach_user_confirm", state),
+
+    new ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs("client_channel_join_rdprdr", channelsToJoin, state),
+
+    new ClientInfoPDU("client_info_req", userName),
+
+    new ServerLicenseErrorPDUValidClient("server_valid_client"),
+
+    new ServerFastPath("server_fastpath"),
+
+    new ServerTpkt("server_tpkt"),
+
+    new ServerX224DataPdu("server_x224_data"),
+
+    // These TPKT and X224 wrappers are connected directly to OUT for handshake
+    // sequence
+        new ClientTpkt("client_tpkt_ot"),
+
+        new ClientX224DataPdu("client_x224_data_ot")
+
+    );
+
+    // Handshake sequence (via SlowPath)
+    link("IN",
+
+    "server_fastpath >tpkt", "server_tpkt",
+
+    "client_connection_req", "server_connection_conf",
+
+    "upgrade_to_ssl",
+
+    "client_initial_conference_create", "server_initial_conference_create",
+
+    "client_erect_domain",
+
+    "server_x224_data",
+
+    "client_atach_user", "server_atach_user_confirm",
+
+    "client_channel_join_rdprdr",
+
+    "client_info_req",
+
+    "server_valid_client"
+
+    );
+
+    // Chain for direct handshake responses (without involving of queue)
+    link("client_x224_data_ot", "client_tpkt_ot", "client_tpkt_ot< OUT");
+
+    // Connect one time outputs to client TPKT input
+    String tpkt_peers[] = new String[] { "client_connection_req", "server_connection_conf", "upgrade_to_ssl", "client_x224_data_ot" };
+    for (String element : tpkt_peers) {
+      link(element + " >otout", element + "< client_tpkt_ot");
+    }
+
+    // Connect one time outputs to client X224 input
+    String x224_peers[] = new String[] { "client_initial_conference_create", "server_initial_conference_create", "client_erect_domain", "client_atach_user",
+        "server_atach_user_confirm", "client_channel_join_rdprdr", "client_info_req", "server_valid_client" };
+    for (String element : x224_peers) {
+      link(element + " >otout", element + "< client_x224_data_ot");
+    }
+
+    //
+    // Transition
+    //
+
+    add(
+    // To transfer packets between input threads and output thread.
+    new Queue("queue"),
+
+    // Slow path: MultiChannel Support
+        new ServerMCSPDU("server_mcs")
+
+    );
+
+    // Last element of handshake sequence will wake up queue and and socket
+    // output pull loop, which will switch links, between socket output and
+    // queue, from push mode to pull mode.
+    link(HANDSHAKE_END + " >queue", "queue", "OUT");
+
+    // Transition from handshake sequence for slow path packets
+    link(HANDSHAKE_END, "server_mcs");
+
+    //
+    // Main network
+    //
+
+    AwtMouseEventSource mouseEventSource = new AwtMouseEventSource("mouse");
+    AwtKeyEventSource keyEventSource = new AwtKeyEventSource("keyboard");
+
+    // Subscribe packet sender to various events
+    canvas.addMouseListener(mouseEventSource);
+    canvas.addMouseMotionListener(mouseEventSource);
+    canvas.addKeyListener(keyEventSource);
+
+    // Add elements
+    add(
+
+    new ServerChannel1003Router("server_channel_1003", state),
+
+    new ServerDemandActivePDU("server_demand_active", screen, state),
+
+    new ClientConfirmActivePDU("client_confirm_active", screen, state),
+
+    new ServerBitmapUpdate("server_bitmap_update"),
+
+    new AwtCanvasAdapter("canvas_adapter", canvas, screen),
+
+    new ServerPaletteUpdate("server_palette", screen),
+
+    keyEventSource, new AwtRdpKeyboardAdapter("keyboard_adapter"),
+
+    mouseEventSource, new AwtRdpMouseAdapter("mouse_adapter"),
+
+    // These FastPath, TPKT, and X224 wrappers are connected to queue
+        new ClientTpkt("client_tpkt_queue"),
+
+        new ClientX224DataPdu("client_x224_data_queue"),
+
+        new ClientFastPathPDU("client_fastpath_queue"));
+
+    // Server packet handlers
+    link("server_mcs >channel_1003", "server_channel_1003");
+    link("server_fastpath >bitmap", "fastpath< server_bitmap_update", "server_bitmap_update< canvas_adapter");
+    link("server_channel_1003 >bitmap", "slowpath< server_bitmap_update");
+
+    link("server_fastpath >palette", "fastpath< server_palette");
+    link("server_channel_1003 >palette", "slowpath< server_palette");
+
+    link("server_channel_1003 >demand_active", "slowpath< server_demand_active");
+    // link("server_demand_active >confirm_active", "client_confirm_active",
+    // "confirm_active< client_channel_1003");
+    link("server_demand_active >confirm_active", "client_confirm_active", "confirm_active< client_x224_data_queue");
+
+    // Link mouse and keyboard to socket via adapters and send them using
+    // FastPath protocol
+    link(mouseEventSource.getId(), "mouse_adapter", "mouse_adapter< client_fastpath_queue");
+    link(keyEventSource.getId(), "keyboard_adapter", "keyboard_adapter< client_fastpath_queue");
+
+    // Link packet wrappers to outgoing queue
+    link("client_fastpath_queue", "client_fastpath_queue< queue");
+    link("client_x224_data_queue", "client_tpkt_queue", "client_tpkt_queue< queue");
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java
new file mode 100644
index 0000000..1e3646a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java
@@ -0,0 +1,74 @@
+// 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.nio.charset.Charset;
+
+public interface RdpConstants {
+
+  /**
+   * Default charset to use when communicating with server using 8 bit strings.
+   */
+  public static final Charset CHARSET_8 = Charset.availableCharsets().get("US-ASCII");
+  
+  /**
+   * Default charset to use when communicating with server using 16 bit strings.
+   */
+  public static final Charset CHARSET_16 = Charset.availableCharsets().get("UTF-16LE");
+  
+
+  /**
+   * Negotiate SSL protocol to use to protect RDP connection.
+   * @see http://msdn.microsoft.com/en-us/library/cc240500.aspx 
+   */
+  public static final int RDP_NEG_REQ_PROTOCOL_SSL = 1;
+  
+  /**
+   * Negotiate CredSSP protocol to use to protect RDP connection.
+   * @see http://msdn.microsoft.com/en-us/library/cc240500.aspx 
+   * When used, client must set @see RDP_NEG_REQ_PROTOCOL_SSL too.
+   */
+  public static final int RDP_NEG_REQ_PROTOCOL_HYBRID = 2;
+  
+  /**
+   * RDP negotiation: flags (not used, always 0).
+   */
+  public static final int RDP_NEG_REQ_FLAGS = 0;
+
+  /**
+   * RDP Negotiation: request.
+   */
+  public static final int RDP_NEG_REQ_TYPE_NEG_REQ = 1;
+  
+  /**
+   * RDP Negotiation: response.
+   */
+  public static final int RDP_NEG_REQ_TYPE_NEG_RSP = 2;
+  
+  /**
+   * RDP Negotiation: failure.
+   */
+  public static final int RDP_NEG_REQ_TYPE_NEG_FAILURE = 3;
+  
+
+  public static final int CHANNEL_IO = 1003;
+  
+  public static final int CHANNEL_RDPRDR = 1004;
+
+
+
+}


[7/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java
new file mode 100644
index 0000000..0189987
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java
@@ -0,0 +1,669 @@
+// 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;
+
+public class ClientMCSConnectInitial extends OneTimeSwitch {
+
+  public ClientMCSConnectInitial(String id) {
+    super(id);
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    throw new RuntimeException("Unexpected packet: " + buf + ".");
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    int length = 1024; // Large enough
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    /* @formatter:off */
+    buf.writeBytes(new byte[] {
+//        - T125: MCSConnect Initial
+//        - MCSConnectInitial: Identifier=Generic Conference Control (0.0.20.124.0.1), ConnectPDULength=254
+//         - ConnectInitialHeader: 
+      (byte)0x7F, (byte)0x65, 
+//          - AsnId: Application Constructed Tag (101)
+//           - HighTag: 
+//              Class:     (01......) Application (1)
+//              Type:      (..1.....) Constructed
+//              TagNumber: (...11111)
+//              TagValueEnd: 101 (0x65)
+      (byte)0x82, (byte)0x01, (byte)0x6C, 
+//          - AsnLen: Length = 364, LengthOfLength = 2
+//             LengthType: LengthOfLength = 2
+//             Length: 364 bytes
+      (byte)0x04, (byte)0x01, (byte)0x01, 
+//         - CallingDomainSelector: 0x1
+//          - AsnOctetStringHeader: 
+//           - AsnId: OctetString type (Universal 4)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..0.....) Primitive
+//               TagValue: (...00100) 4
+//           - AsnLen: Length = 1, LengthOfLength = 0
+//              Length: 1 bytes, LengthOfLength = 0
+//            OctetStream: 0x1
+      (byte)0x04, (byte)0x01, (byte)0x01,
+//         - CalledDomainSelector: 0x1
+//          - AsnOctetStringHeader: 
+//           - AsnId: OctetString type (Universal 4)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..0.....) Primitive
+//               TagValue: (...00100) 4
+//           - AsnLen: Length = 1, LengthOfLength = 0
+//              Length: 1 bytes, LengthOfLength = 0
+//            OctetStream: 0x1
+      (byte)0x01, (byte)0x01, (byte)0xFF,
+//         - UpwardFlag: True
+//          - AsnBooleanHeader: 
+//           - AsnId: Boolean type (Universal 1)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..0.....) Primitive
+//               TagValue: (...00001) 1
+//           - AsnLen: Length = 1, LengthOfLength = 0
+//              Length: 1 bytes, LengthOfLength = 0
+//            Tf: 255 (0xFF)
+
+//
+//         - TargetParameters: Length = 26, LengthOfLength = 0
+      (byte)0x30, (byte)0x1A, 
+//          - DomainParametersHeader: 0x1
+//           - AsnId: Sequence and SequenceOf types (Universal 16)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..1.....) Constructed
+//               TagValue: (...10000) 16
+//           - AsnLen: Length = 26, LengthOfLength = 0
+//              Length: 26 bytes, LengthOfLength = 0
+      (byte)0x02, (byte)0x01, (byte)0x22, 
+//          - ChannelIds: 34
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 34 (0x22)
+      (byte)0x02, (byte)0x01, (byte)0x02,
+//          - UserIDs: 2
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 2 (0x2)
+      (byte)0x02, (byte)0x01, (byte)0x00,
+//          - TokenIds: 0
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 0 (0x0)
+      (byte)0x02, (byte)0x01, (byte)0x01,
+//          - NumPriorities: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x01, (byte)0x00,
+//          - MinThroughput: 0
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 0 (0x0)
+      (byte)0x02, (byte)0x01, (byte)0x01,
+//          - Height: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF,
+//          - MCSPDUsize: 65535
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 3, LengthOfLength = 0
+//               Length: 3 bytes, LengthOfLength = 0
+//             AsnInt: 65535 (0xFFFF)
+      (byte)0x02, (byte)0x01, (byte)0x02, 
+//          - protocolVersion: 2
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 2 (0x2)
+
+//      
+//         - MinimumParameters: Length = 25, LengthOfLength = 0
+      (byte)0x30, (byte)0x19, 
+//          - DomainParametersHeader: 0x1
+//           - AsnId: Sequence and SequenceOf types (Universal 16)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..1.....) Constructed
+//               TagValue: (...10000) 16
+//           - AsnLen: Length = 25, LengthOfLength = 0
+//              Length: 25 bytes, LengthOfLength = 0
+      (byte)0x02, (byte)0x01, (byte)0x01,
+//          - ChannelIds: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x01, (byte)0x01,
+//          - UserIDs: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x01, (byte)0x01,
+//          - TokenIds: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x01, (byte)0x01,
+//          - NumPriorities: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x01, (byte)0x00,
+//          - MinThroughput: 0
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 0 (0x0)
+      (byte)0x02, (byte)0x01, (byte)0x01, 
+//          - Height: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x02, (byte)0x04, (byte)0x20, 
+//          - MCSPDUsize: 1056
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 2, LengthOfLength = 0
+//               Length: 2 bytes, LengthOfLength = 0
+//             AsnInt: 1056 (0x420)
+      (byte)0x02, (byte)0x01, (byte)0x02, 
+//          - protocolVersion: 2
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 2 (0x2)
+//         - MaximumParameters: Length = 31, LengthOfLength = 0
+//          - DomainParametersHeader: 0x1
+      (byte)0x30, (byte)0x1F,
+//           - AsnId: Sequence and SequenceOf types (Universal 16)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..1.....) Constructed
+//               TagValue: (...10000) 16
+//           - AsnLen: Length = 31, LengthOfLength = 0
+//              Length: 31 bytes, LengthOfLength = 0
+      (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, 
+//          - ChannelIds: 65535
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 3, LengthOfLength = 0
+//               Length: 3 bytes, LengthOfLength = 0
+//             AsnInt: 65535 (0xFFFF)
+      (byte)0x02, (byte)0x02, (byte)0xFC, (byte)0x17, 
+//          - UserIDs: 64535
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 2, LengthOfLength = 0
+//               Length: 2 bytes, LengthOfLength = 0
+//             AsnInt: 64535 (0xFC17)
+      (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF,
+//          - TokenIds: 65535
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 3, LengthOfLength = 0
+//               Length: 3 bytes, LengthOfLength = 0
+//             AsnInt: 65535 (0xFFFF)
+      (byte)0x02, (byte)0x01, (byte)0x01, 
+//          - NumPriorities: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x01, (byte)0x00, 
+//          - MinThroughput: 0
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 0 (0x0)
+      (byte)0x02, (byte)0x01, (byte)0x01, 
+//          - Height: 1
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 1 (0x1)
+      (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, 
+//          - MCSPDUsize: 65535
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 3, LengthOfLength = 0
+//               Length: 3 bytes, LengthOfLength = 0
+//             AsnInt: 65535 (0xFFFF)
+      (byte)0x02, (byte)0x01, (byte)0x02, 
+//          - protocolVersion: 2
+//           - AsnIntegerHeader: 
+//            - AsnId: Integer type (Universal 2)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00010) 2
+//            - AsnLen: Length = 1, LengthOfLength = 0
+//               Length: 1 bytes, LengthOfLength = 0
+//             AsnInt: 2 (0x2)
+//         - UserData: Identifier=Generic Conference Contro (0.0.20.124.0.1), ConnectPDULength=254
+//          - UserDataHeader: 
+      (byte)0x04, (byte)0x82, (byte)0x01, (byte)0x07, 
+//           - AsnId: OctetString type (Universal 4)
+//            - LowTag: 
+//               Class:    (00......) Universal (0)
+//               Type:     (..0.....) Primitive
+//               TagValue: (...00100) 4
+//           - AsnLen: Length = 263, LengthOfLength = 2
+//              LengthType: LengthOfLength = 2
+//              Length: 263 bytes
+      (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7C, (byte)0x00, (byte)0x01, 
+//          - AsnBerObjectIdentifier: Generic Conference Contro (0.0.20.124.0.1)
+//           - AsnObjectIdentifierHeader: 
+//            - AsnId: Reserved for use by the encoding rules (Universal 0)
+//             - LowTag: 
+//                Class:    (00......) Universal (0)
+//                Type:     (..0.....) Primitive
+//                TagValue: (...00000) 0
+//            - AsnLen: Length = 5, LengthOfLength = 0
+//               Length: 5 bytes, LengthOfLength = 0
+//             First: 0 (0x0)
+//             Final: 20 (0x14)
+//             Final: 124 (0x7C)
+//             Final: 0 (0x0)
+//             Final: 1 (0x1)
+      (byte)0x80, (byte)0xFE, 
+//          - ConnectPDULength: 254
+//             Align: No Padding
+//             Length: 254
+      (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10,
+//          - ConnectGCCPDU: conferenceCreateRequest
+//             ExtensionBit: 0 (0x0)
+//           - ChoiceValue: conferenceCreateRequest
+//              Value: (000.....) 0x0
+//           - conferenceCreateRequest: 
+//              ExtensionBit: 0 (0x0)
+//              convenerPasswordPresent: 0 (0x0)
+//              passwordPresent: 0 (0x0)
+//              conductorPrivilegesPresent: 0 (0x0)
+//              conductedPrivilegesPresent: 0 (0x0)
+//              nonConductedPrivilegesPresent: 0 (0x0)
+//              conferenceDescriptionPresent: 0 (0x0)
+//              callerIdentifierPresent: 0 (0x0)
+//              userDataPresent: 1 (0x1)
+//            - conferenceName: 
+//               ExtensionBit: 0 (0x0)
+//               textPresent: 0 (0x0)
+//             - numeric: 1
+//              - SimpleNumericString: 1
+//               - NumericString: 1
+//                - Align: No Padding
+//                   Padding1: (0.......) 0x0
+//                - Length: 1
+//                   Value: (00000000) 0x0
+//                - Restrictedstr: 1
+//                   FourBits: (0001....) 0x1
+//            - lockedConference: False
+//               Value: False 0.......
+//            - listedConference: False
+//               Value: False 0.......
+//            - conductibleConference: False
+//               Value: False 0.......
+//            - TerminationMethod: automatic
+//               ExtensionBit: 0 (0x0)
+//             - RootIndex: 0
+//                Value: (0.......) 0x0
+//            - userData: 
+      (byte)0x00, (byte)0x01, 
+//             - Size: 1
+//              - Align: No Padding
+//                 Padding7: (0000000.) 0x0
+//                Length: 1
+//             - UserData: 0x44756361
+      (byte)0xC0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, 
+//                valuePresent: 1 (0x1)
+//              - key: h221NonStandard "Duca"
+//               - ChoiceValue: h221NonStandard
+//                  Value: (1.......) 0x1
+//               - h221NonStandard: 
+//                - H221NonStandardIdentifier: length: 4
+//                 - ConstrainedLength: 4
+//                    Value: (00000000) 0x0
+//                 - Align: No Padding
+//                    Padding6: (000000..) 0x0
+//                   Value: Binary Large Object (4 Bytes) "Duca"
+//              - ClientMcsConnectInitialPdu: 
+      (byte)0x80, (byte)0xF0, 
+//               - RDPGCCUserDataRequestLength: 240
+//                  Align: No Padding
+//                  Length: 240
+//               - TsUd: CS_CORE
+      (byte)0x01, (byte)0xC0, (byte)0xD8, (byte)0x00, 
+//                - TsUdHeader: Type = CS_CORE, Length = 216
+//                   Type: CS_CORE
+//                   Length: 216 (0xD8)
+//                - TsUdCsCore: 
+      (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00, 
+//                   Version: RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 
+      (byte)0x00, (byte)0x04, 
+//                   DesktopWidth: 1024 (0x400)
+      (byte)0x00, (byte)0x03, 
+//                   DesktopHeight: 768 (0x300)
+      (byte)0x01, (byte)0xCA, 
+//                   ColorDepth: 8 bpp
+      (byte)0x03, (byte)0xAA, 
+//                   SASSequence: 0xaa03, SHOULD be set to RNS_UD_SAS_DEL(0xAA03)
+      (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, 
+//                   KeyboardLayout: Language: English, Location: United States
+      (byte)0x28, (byte)0x0A, (byte)0x00, (byte)0x00, 
+//                   ClientBuild: 2600 (0xA28)
+      (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6C, (byte)0x00, (byte)0x6C, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//                   ClientName: apollo3
+      (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//                   KeyboardType: Undefined value: 0
+      (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//                   KeyboardSubType: 0 (0x0)
+      (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//                   KeyboardFunctionKey: 0 (0x0)
+      (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//                   ImeFileName: 
+      (byte)0x01, (byte)0xCA, 
+//                   PostBeta2ColorDepth: 8 bpp
+      (byte)0x01, (byte)0x00, 
+//                   ClientProductId: 0x1, SHOULD be set to initialized to 1
+      (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//                   SerialNumber: 0x0, SHOULD be set to 0
+      (byte)0x10, (byte)0x00, 
+//                   HighColorDepth: 16-bit 565 RGB
+      (byte)0x07, (byte)0x00, 
+//                 - SupportedColorDepth: 7 (0x7)
+//                    Support24BPP: (...............1) Support 24BPP
+//                    Support16BPP: (..............1.) Support 16BPP
+//                    Support15BPP: (.............1..) Support 15BPP
+//                    Support32BPP: (............0...) Not Support 32BPP
+//                    Reserved:     (000000000000....)
+      (byte)0x01, (byte)0x00, 
+//                 - EarlyCapabilityFlags: 1 (0x1)
+//                    SupportSetErrorPdu:      (...............1) Indicates that the client supports the Set Error Info PDU
+//                    Want32BppSession:        (..............0.) Client is not requesting 32BPP session
+//                    SupportStatusInfoPdu:    (.............0..) Client not supports the Server Status Info PDU
+//                    StrongAsymmetricKeys:    (............0...) Not support asymmetric keys larger than 512-bits
+//                    Unused:                  (...........0....)
+//                    ValidConnection:         (..........0.....) Not Indicates ConnectionType field contains valid data
+//                    SupportMonitorLayoutPdu: (.........0......) Not Indicates that the client supports the Monitor Layout PDU
+//                    Unused2:                 (000000000.......)
+      (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//      ClientDigProductId: 
+(byte)0x00, 
+//      connectionType: invalid connection type
+(byte)0x00, 
+//      pad1octet: 0 (0x0)
+(byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, 
+//      ServerSelectedProtocols: TLS 1.0
+//
+//  - TsUd: CS_CLUSTER
+//   - TsUdHeader: Type = CS_CLUSTER, Length = 12
+(byte)0x04, (byte)0xC0, 
+//      Type: CS_CLUSTER
+(byte)0x0C, (byte)0x00, 
+//      Length: 12 (0xC)
+(byte)0x0D, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+//   - TsUdCsCluster: 
+//    - Flags: 13 (0xD)
+//       RedirectedSupported: (...............................1) Support Redirected
+//       SessionIDFieldValid: (..............................0.) SessionID Field not Valid
+//       SupportedVersion:    (..........................0011..) REDIRECTION_VERSION4
+//       RedirectedSmartcard: (.........................0......) Not Logon with Smartcard
+//       Unused:           (0000000000000000000000000.......)
+//      RedirectedSessionID: 0 (0x0)
+//
+//  - TsUd: CS_SECURITY
+//   - TsUdHeader: Type = CS_SECURITY, Length = 12
+(byte)0x02, (byte)0xC0, 
+//      Type: CS_SECURITY
+(byte)0x0C, (byte)0x00, 
+//      Length: 12 (0xC)
+//      
+//   - TsUdCsSec: 
+(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//    - EncryptionMethod: 
+//       Support40Bit:  (...............................0) Not Support 
+//       Support128Bit: (..............................0.) Not Support 128-bit
+//       Reserved1:     (.............................0..)
+//       Support56Bit:  (............................0...) Not Support 56-bit
+//       SupportFIPS:   (...........................0....) Not Support FIPS Compliant
+//       Reserved2:     (000000000000000000000000000.....)
+(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+//    - ExtEncryptionMethod: 
+//       Support40Bit:  (...............................0) Not Support 
+//       Support128Bit: (..............................0.) Not Support 128-bit
+//       Reserved1:     (.............................0..)
+//       Support56Bit:  (............................0...) Not Support 56-bit
+//       SupportFIPS:   (...........................0....) Not Support FIPS Compliant
+//       Reserved2:     (000000000000000000000000000.....)
+    });
+    /* @formatter:on */
+
+    buf.length = buf.cursor;
+
+    pushDataToOTOut(buf);
+
+    switchOff();
+  }
+
+  /**
+   * Example.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc240836.aspx
+   */
+  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[] {
+        // TPKT: TPKT version = 3
+        (byte) 0x03,  (byte) 0x00,  
+        // TPKT: Packet length: 378 bytes
+        (byte) 0x01,  (byte) 0x78, 
+        
+        // X.224: Length indicator = 2
+        (byte) 0x02,  
+        // X.224: Type: Data TPDU
+        (byte) 0xf0,  
+        // X.224: EOT
+        (byte) 0x80,  
+        
+        // Captured packet
+        (byte)0x7f, (byte)0x65, (byte)0x82, (byte)0x01, (byte)0x6c, (byte)0x04, (byte)0x01, (byte)0x01, (byte)0x04,
+        (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0xff, (byte)0x30, (byte)0x1a, (byte)0x02, (byte)0x01, (byte)0x22, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x01, (byte)0x00,
+        (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01,
+        (byte)0x02, (byte)0x30, (byte)0x19, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02,
+        (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x04, (byte)0x20, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x30, (byte)0x1f, (byte)0x02, (byte)0x03,
+        (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x02, (byte)0xfc, (byte)0x17, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02,
+        (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x82, (byte)0x01,
+        (byte)0x07, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7c, (byte)0x00, (byte)0x01, (byte)0x80, (byte)0xfe, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x01,
+        (byte)0xc0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, (byte)0x80, (byte)0xf0, (byte)0x01, (byte)0xc0, (byte)0xd8, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00,
+        (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x03, (byte)0x01, (byte)0xca, (byte)0x03, (byte)0xaa, (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x0a, (byte)0x00, (byte)0x00,
+        (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0xca, (byte)0x01, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x04, (byte)0xc0, (byte)0x0c, (byte)0x00, (byte)0x0d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0xc0, (byte)0x0c, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+    };
+    /* @formatter:on */
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+    Element todo = new ClientMCSConnectInitial("ClientMCSConnectInitial");
+    Element x224 = new ClientX224DataPdu("x224");
+    Element tpkt = new ClientTpkt("tpkt");
+
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, todo, x224, tpkt, sink, mainSink);
+    pipeline.link("source", "ClientMCSConnectInitial", "mainSink");
+    pipeline.link("ClientMCSConnectInitial >" + OTOUT, "x224", "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java
new file mode 100644
index 0000000..4a0fd04
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java
@@ -0,0 +1,190 @@
+// 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;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240683.aspx
+ */
+public class ClientMCSErectDomainRequest extends OneTimeSwitch {
+
+  public ClientMCSErectDomainRequest(String id) {
+    super(id);
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    throw new RuntimeException("Unexpected packet: " + buf + ".");
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    int length = 5;
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    buf.writeByte(0x04); // Erect Domain Request
+    
+    // Client SHOULD initialize both the subHeight and subinterval fields of the MCS Erect Domain Request PDU to zero.
+    
+    buf.writeByte(1); // ErectDomainRequest::subHeight length = 1 byte
+    buf.writeByte(0); // ErectDomainRequest::subHeight
+    
+    buf.writeByte(1); // ErectDomainRequest::subInterval length = 1 byte
+    buf.writeByte(0); // ErectDomainRequest::subInterval
+    
+    
+    pushDataToOTOut(buf);
+
+    switchOff();
+  }
+
+  /**
+   * Example.
+   * @see http://msdn.microsoft.com/en-us/library/cc240837.aspx
+   */
+  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[] {
+
+        0x03, 0x00, 0x00, 0x0c,  //  TPKT Header (length = 12 bytes)
+        0x02, (byte) 0xf0, (byte) 0x80,  //  X.224 Data TPDU
+        
+        // PER encoded (ALIGNED variant of BASIC-PER) PDU contents:
+        0x04, 0x01, 0x00, 0x01, 0x00,
+         
+        // 0x04:
+        // 0 - --\
+        // 0 -   |
+        // 0 -   | CHOICE: From DomainMCSPDU select erectDomainRequest (1) 
+        // 0 -   | of type ErectDomainRequest
+        // 0 -   |
+        // 1 - --/
+        // 0 - padding
+        // 0 - padding
+        
+        // 0x01:
+        // 0 - --\
+        // 0 -   |
+        // 0 -   |
+        // 0 -   | ErectDomainRequest::subHeight length = 1 byte
+        // 0 -   |
+        // 0 -   |
+        // 0 -   |
+        // 1 - --/
+        
+        // 0x00:
+        // 0 - --\
+        // 0 -   |
+        // 0 -   |
+        // 0 -   | ErectDomainRequest::subHeight = 0
+        // 0 -   |
+        // 0 -   |
+        // 0 -   |
+        // 0 - --/
+        
+        // 0x01:
+        // 0 - --\
+        // 0 -   |
+        // 0 -   |
+        // 0 -   | ErectDomainRequest::subInterval length = 1 byte
+        // 0 -   |
+        // 0 -   |
+        // 0 -   |
+        // 1 - --/
+        
+        // 0x00:
+        // 0 - --\
+        // 0 -   |
+        // 0 -   |
+        // 0 -   | ErectDomainRequest::subInterval = 0
+        // 0 -   |
+        // 0 -   |
+        // 0 -   |
+        // 0 - --/
+
+        
+    };
+    /* @formatter:on */
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+    Element todo = new ClientMCSErectDomainRequest("TODO");
+    Element x224 = new ClientX224DataPdu("x224");
+    Element tpkt = new ClientTpkt("tpkt");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, todo, x224, tpkt, sink, mainSink);
+    pipeline.link("source", "TODO", "mainSink");
+    pipeline.link("TODO >" + OTOUT, "x224", "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}
+
+/*
+ * 03 00 00 0C 02 F0 80 04 01 00 01 00
+
+  Frame: Number = 14, Captured Frame Length = 69, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 12
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 12 (0xC)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: Erect Domain Request, SubHeight = 0, SubInterval = 0
+  - MCSHeader: Type=Erect Domain Request
+   - Type: Erect Domain Request
+    - RootIndex: 1
+       Value: (000001..) 0x1
+  - MCSErectDomainRequest: SubHeight = 0, SubInterval = 0
+   - SubHeight: 0x0
+    - Length: 1
+     - Align: No Padding
+        Padding2: (00......) 0x0
+       Length: 1
+      Value: 0 (0x0)
+   - SubInterval: 0x0
+    - Length: 1
+       Align: No Padding
+       Length: 1
+      Value: 0 (0x0)
+
+ */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java
new file mode 100644
index 0000000..651d002
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java
@@ -0,0 +1,50 @@
+// 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;
+
+
+/**
+ * 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 TPKT Unknown packet",      "03"),
+//    new Pair("Client UNKNOWN PACKET (ERROR)",   ".*"),
+    // @formatter:on
+
+  };
+
+  public ClientPacketSniffer(String id) {
+    super(id, clientRegexps);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java
new file mode 100644
index 0000000..9631976
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java
@@ -0,0 +1,248 @@
+// 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;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240489.aspx
+ */
+public class ClientSynchronizePDU extends OneTimeSwitch {
+
+  public ClientSynchronizePDU(String id) {
+    super(id);
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    throw new RuntimeException("Unexpected packet: " + buf + ".");
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    int length = 1024; // Large enough
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    /* @formatter:off */
+    buf.writeBytes(new byte[] {
+        // MCS send data request
+        (byte)0x64,
+        // Initiator: 1004 (1001+3)
+        (byte)0x00, (byte)0x03,
+        // Channel ID: 1003 (I/O Channel)
+        (byte)0x03, (byte)0xeb,
+        // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+        (byte)0x70, 
+        // Data length:  22 bytes (0x16, variable length field)
+        (byte)0x80,  (byte)0x16, 
+        
+        // RDP: total length: 22 bytes (LE)
+        (byte)0x16, (byte)0x00, 
+        
+        // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE)
+        (byte)0x17, (byte)0x00,
+        
+        // PDU source: 1007 (LE)
+        (byte)0xec, (byte)0x03,
+        // Share ID: 0x000103ea (LE)
+        (byte)0xea, (byte)0x03, (byte)0x01,  (byte)0x00,
+        // Padding: 1 byte
+        (byte)0x00,
+        // Stream ID: STREAM_LOW (1)
+        (byte)0x01, 
+        // uncompressedLength : 8 bytes (LE)
+        (byte)0x08, (byte)0x00,
+        // pduType2 = PDUTYPE2_SYNCHRONIZE (31)
+        (byte)0x1f, 
+        // generalCompressedType: 0
+        (byte)0x00,
+        // generalCompressedLength: 0 (LE?)
+        (byte)0x00, (byte)0x00,
+        //  messageType: SYNCMSGTYPE_SYNC (1) (LE)
+        (byte)0x01, (byte)0x00, 
+        // targetUser: 0x03ea
+        (byte)0xea, (byte)0x03,
+    });
+    /* @formatter:on */
+
+    // Trim buffer to actual length of data written
+    buf.trimAtCursor();
+
+    pushDataToOTOut(buf);
+
+    switchOff();
+  }
+
+  /**
+   * Example.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc240841.aspx
+   */
+  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[] {
+        // TPKT
+        (byte)0x03, (byte)0x00,
+        // TPKT length: 37 bytes
+        (byte)0x00, (byte)0x25,
+        // X224 Data PDU 
+        (byte)0x02, (byte)0xf0, (byte)0x80,
+        
+        // MCS send data request
+        (byte)0x64,
+        // Initiator: 1004 (1001+3)
+        (byte)0x00, (byte)0x03,
+        // Channel ID: 1003 (I/O Channel)
+        (byte)0x03, (byte)0xeb,
+        // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+        (byte)0x70, 
+        // Data length:  22 bytes (0x16, variable length field)
+        (byte)0x80,  (byte)0x16, 
+        
+        // RDP: total length: 22 bytes (LE)
+        (byte)0x16, (byte)0x00, 
+        // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE)
+        (byte)0x17, (byte)0x00,
+        // PDU source: 1007 (LE)
+        (byte)0xec, (byte)0x03,
+        // Share ID: 0x000103ea (LE)
+        (byte)0xea, (byte)0x03, (byte)0x01,  (byte)0x00,
+        // Padding: 1 byte
+        (byte)0x00,
+        // Stream ID: STREAM_LOW (1)
+        (byte)0x01, 
+        // uncompressedLength : 8 bytes (LE)
+        (byte)0x08, (byte)0x00,
+        // pduType2 = PDUTYPE2_SYNCHRONIZE (31)
+        (byte)0x1f, 
+        // generalCompressedType: 0
+        (byte)0x00,
+        // generalCompressedLength: 0 (LE?)
+        (byte)0x00, (byte)0x00,
+        //  messageType: SYNCMSGTYPE_SYNC (1) (LE)
+        (byte)0x01, (byte)0x00, 
+        // targetUser: 0x03ea
+        (byte)0xea, (byte)0x03,
+        
+    };
+    /* @formatter:on */
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+    Element todo = new ClientSynchronizePDU("TODO");
+    Element x224 = new ClientX224DataPdu("x224");
+    Element tpkt = new ClientTpkt("tpkt");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, todo, x224, tpkt, sink, mainSink);
+    pipeline.link("source", "TODO", "mainSink");
+    pipeline.link("TODO >" + OTOUT, "x224", "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}
+
+/*
+ * @formatting:off
+
+ * 03 00 00 25 02 F0 80 64 00 03 03 EB 70 80 16 16 00 17 00 EC 03 EA 03 01 00 00 01 08 00 1F 00 00 00 01 00 EA 03 
+
+  Frame: Number = 40, Captured Frame Length = 94, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 37
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 37 (0x25)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: Data Packet
+  - MCSHeader: Type=Send Data Request, UserID=1004, ChannelID=1003
+   - Type: Send Data Request
+    - RootIndex: 25
+       Value: (011001..) 0x19
+   - UserID: 0x3ec
+    - UserID: 0x3ec
+     - ChannelId: 1004
+      - Align: No Padding
+         Padding2: (00......) 0x0
+        Value: 3 (0x3)
+   - Channel: 0x3eb
+    - ChannelId: 1003
+       Align: No Padding
+       Value: 1003 (0x3EB)
+   - DataPriority: high
+    - DataPriority: high
+     - RootIndex: 1
+        Value: (01......) 0x1
+   - Segmentation: Begin End
+      Begin: (1.......) Begin
+      End:   (.1......) End
+   - Length: 22
+    - Align: No Padding
+       Padding4: (0000....) 0x0
+      Length: 22
+    RDP: RDPBCGR
+- RDPBCGR: SynchronizePDU
+  - SlowPathPacket: SynchronizePDU 
+   - SlowPath: Type = TS_PDUTYPE_DATAPDU
+    - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU
+       TotalLength: 22 (0x16)
+     - PDUType: 23 (0x17)
+        Type:            (............0111) TS_PDUTYPE_DATAPDU
+        ProtocolVersion: (000000000001....) 1
+       PDUSource: 1004 (0x3EC)
+    - SlowPathIoPacket: 0x0
+     - ShareDataHeader: TS_PDUTYPE2_SYNCHRONIZE
+        ShareID: 66538 (0x103EA)
+        Pad1: 0 (0x0)
+        StreamID: TS_STREAM_LOW
+        UncompressedLength: 8 (0x8)
+        PDUType2: TS_PDUTYPE2_SYNCHRONIZE
+      - CompressedType: Not Compressed
+         MPPC:       (....0000) MPPC 8K
+         Reserved:   (...0....)
+         Compressed: (..0.....) Not Compressed
+         Front:      (.0......) Not At Front
+         Flush:      (0.......) Not Flushed
+        CompressedLength: 0 (0x0)
+     - TsSynchronizePDU: 0x1
+        MessageType: 0x1, MUST be set to SYNCMSGTYPE_SYNC (1)
+        TargetUser: 1002 (0x3EA)
+ */
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java
new file mode 100644
index 0000000..c92d832
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java
@@ -0,0 +1,54 @@
+// 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 ClientTpkt extends BaseElement {
+
+  public ClientTpkt(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 + ".");
+
+    if (buf.length + 4 > 65535)
+      throw new RuntimeException("Packet is too long for TPKT (max length 65535-4): " + buf + ".");
+
+    ByteBuffer data = new ByteBuffer(4);
+    // TPKT version
+    data.writeByte(3);
+    // Reserved
+    data.writeByte(0);
+    // Packet length, including length of the header
+    data.writeShort(buf.length + 4);
+
+    buf.prepend(data);
+    data.unref();
+
+    pushDataToPad(STDOUT, buf);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java
new file mode 100644
index 0000000..48ac089
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java
@@ -0,0 +1,156 @@
+// 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;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240470.aspx
+ * @see http://msdn.microsoft.com/en-us/library/cc240663.aspx
+ */
+public class ClientX224ConnectionRequestPDU 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;
+
+  /**
+   * Reconnection cookie.
+   */
+  protected String userName;
+
+  public ClientX224ConnectionRequestPDU(String id, String userName) {
+    super(id);
+    this.userName = userName;
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    throw new RuntimeException("Unexpected packet: " + buf + ".");
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    // Length of packet without length field
+    int length = 33 + userName.length();
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    // Type (high nibble) = 0xe = CR TPDU; credit (low nibble) = 0
+    buf.writeByte(X224_TPDU_CONNECTION_REQUEST);
+
+    buf.writeShort(0); // Destination reference = 0
+    buf.writeShort(0); // Source reference = 0
+    buf.writeByte(0); // Class and options = 0
+    buf.writeString("Cookie: mstshash=" + userName + "\r\n", RdpConstants.CHARSET_8); // Cookie
+
+    // RDP_NEG_REQ::type
+    buf.writeByte(RdpConstants.RDP_NEG_REQ_TYPE_NEG_REQ);
+    // RDP_NEG_REQ::flags (0)
+    buf.writeByte(RdpConstants.RDP_NEG_REQ_FLAGS);
+    // RDP_NEG_REQ::length (constant: 8) short int in LE format
+    buf.writeByte(0x08);
+    buf.writeByte(0x00);
+
+    // RDP_NEG_REQ: Requested protocols: PROTOCOL_SSL
+    buf.writeIntLE(RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL);
+
+    // Calculate length of packet and prepend it to buffer
+    ByteBuffer data = new ByteBuffer(5);
+
+    // Write length
+    data.writeVariableIntLE(buf.length);
+
+    // Reset length of buffer to actual length of data written
+    data.length = data.cursor;
+
+    buf.prepend(data);
+    data.unref();
+
+    pushDataToOTOut(buf);
+
+    switchOff();
+  }
+
+  /**
+   * Example.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc240842.aspx
+   * @see http://msdn.microsoft.com/en-us/library/cc240500.aspx
+   */
+  public static void main(String args[]) {
+    // System.setProperty("streamer.Link.debug", "true");
+    System.setProperty("streamer.Element.debug", "true");
+    // System.setProperty("streamer.Pipeline.debug", "true");
+
+    String cookie = "eltons";
+
+    byte[] packet = new byte[] {
+
+    0x03, // TPKT Header: version = 3
+        0x00, // TPKT Header: Reserved = 0
+        0x00, // TPKT Header: Packet length - high part
+        0x2c, // TPKT Header: Packet length - low part (total = 44 bytes)
+        0x27, // X.224: Length indicator (39 bytes)
+        (byte) 0xe0, // X.224: Type (high nibble) = 0xe = CR TPDU;
+                     // credit (low nibble) = 0
+        0x00, 0x00, // X.224: Destination reference = 0
+        0x00, 0x00, // X.224: Source reference = 0
+        0x00, // X.224: Class and options = 0
+
+        'C', 'o', 'o', 'k', 'i', 'e', ':', ' ', 'm', 's', 't', 's', 'h', 'a', 's', 'h', '=', 'e', 'l', 't', 'o', 'n', 's', // "Cookie: mstshash=eltons"
+        '\r', '\n', // -Cookie terminator sequence
+
+        0x01, // RDP_NEG_REQ::type (TYPE_RDP_NEG_REQ)
+        0x00, // RDP_NEG_REQ::flags (0)
+        0x08, 0x00, // RDP_NEG_REQ::length (8 bytes)
+        0x01, 0x00, 0x00, 0x00 // RDP_NEG_REQ: Requested protocols
+                               // (PROTOCOL_SSL in little endian format)
+    };
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+    Element cr = new ClientX224ConnectionRequestPDU("cr", cookie);
+    Element tpkt = new ClientTpkt("tpkt");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, cr, tpkt, sink, mainSink);
+    pipeline.link("source", "cr", "mainSink");
+    pipeline.link("cr >" + OTOUT, "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java
new file mode 100644
index 0000000..2cf9b72
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java
@@ -0,0 +1,54 @@
+// 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 ClientX224DataPdu extends BaseElement {
+
+  public static final int X224_TPDU_DATA = 0xF0;
+  public static final int X224_TPDU_LAST_DATA_UNIT = 0x80;
+
+  public ClientX224DataPdu(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 + ".");
+
+    ByteBuffer data = new ByteBuffer(3);
+    // X224 header
+    data.writeByte(2); // Header length indicator
+    data.writeByte(X224_TPDU_DATA);
+    data.writeByte(X224_TPDU_LAST_DATA_UNIT);
+
+    buf.prepend(data);
+    data.unref();
+
+    pushDataToPad(STDOUT, buf);
+  }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java
new file mode 100644
index 0000000..62242de
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java
@@ -0,0 +1,27 @@
+// 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;
+
+public class HandshakeEnd extends BaseElement {
+
+  public HandshakeEnd(String id) {
+    super(id);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java
new file mode 100644
index 0000000..b141917
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java
@@ -0,0 +1,197 @@
+// 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.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+public class MockServer implements Runnable {
+
+    private boolean shutdown = false;
+    private ServerSocket serverSocket;
+    private Packet[] packets;
+    private Throwable exception;
+    private boolean shutdowned;
+
+    /**
+     * Set to true to enable debugging messages.
+     */
+    protected boolean verbose = System.getProperty("rdpclient.MockServer.debug", "false").equals("true");
+
+    public MockServer(Packet packets[]) {
+	this.packets = packets;
+    }
+
+    public void start() throws IOException {
+	serverSocket = new ServerSocket(0);
+
+	shutdown = false;
+	exception = null;
+	shutdowned = false;
+
+	new Thread(this).start();
+    }
+
+    public void run() {
+
+	try {
+	    Socket socket = serverSocket.accept();
+
+	    InputStream is = socket.getInputStream();
+	    OutputStream os = socket.getOutputStream();
+
+	    try {
+		for (int i = 0; i < packets.length && !shutdown; i++) {
+
+		    Packet packet = packets[i];
+		    switch (packet.type) {
+		    case CLIENT: {
+			// Read client data and compare it with mock data
+			// (unless "ignore" option is set)
+			byte actualData[] = new byte[packet.data.length];
+			int actualDataLength = is.read(actualData);
+
+			if (verbose)
+			    System.out.println("[" + this + "] INFO: Data is read: {" + Arrays.toString(Arrays.copyOf(actualData, actualDataLength)) + "}.");
+
+			if (!packet.ignore) {
+			    // Compare actual data with expected data
+			    if (actualDataLength != packet.data.length) {
+				throw new AssertionError("Actual length of client request for packet #" + (i + 1) + " (\"" + packet.id + "\")"
+					+ " does not match length of expected client request. Actual length: " + actualDataLength + ", expected legnth: "
+					+ packet.data.length + ".");
+			    }
+
+			    for (int j = 0; j < packet.data.length; j++) {
+
+				if (packet.data[j] != actualData[j]) {
+				    throw new AssertionError("Actual byte #" + (j + 1) + " of client request for packet #" + (i + 1) + " (\"" + packet.id
+					    + "\")" + " does not match corresponding byte of expected client request. Actual byte: " + actualData[j]
+					    + ", expected byte: " + packet.data[j] + ".");
+				}
+			    }
+			}
+			break;
+		    }
+		    case SERVER: {
+			// Send mock data to client
+			os.write(packet.data);
+
+			if (verbose)
+			    System.out.println("[" + this + "] INFO: Data is written: {" + Arrays.toString(packet.data) + "}.");
+
+			break;
+		    }
+		    case UPGRADE_TO_SSL: {
+			// Attach SSL context to socket
+
+			final SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+			SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, null, serverSocket.getLocalPort(), true);
+			sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
+			sslSocket.setUseClientMode(false);
+			sslSocket.startHandshake();
+			is = sslSocket.getInputStream();
+			os = sslSocket.getOutputStream();
+
+			break;
+		    }
+		    default:
+			throw new RuntimeException("Unknown packet type: " + packet.type);
+		    }
+
+		}
+	    } finally {
+		try {
+		    is.close();
+		} catch (Throwable e) {
+		}
+		try {
+		    os.close();
+		} catch (Throwable e) {
+		}
+		try {
+		    socket.close();
+		} catch (Throwable e) {
+		}
+		try {
+		    serverSocket.close();
+		} catch (Throwable e) {
+		}
+	    }
+	} catch (Throwable e) {
+	    System.err.println("Error in mock server: " + e.getMessage());
+	    e.printStackTrace(System.err);
+	    exception = e;
+	}
+	shutdowned = true;
+	if (verbose)
+	    System.out.println("[" + this + "] INFO: Mock server shutdowned.");
+
+    }
+
+    public void shutdown() {
+	shutdown = true;
+    }
+
+    public InetSocketAddress getAddress() {
+	return (InetSocketAddress) serverSocket.getLocalSocketAddress();
+    }
+
+    public Throwable getException() {
+	return exception;
+    }
+
+    public static class Packet {
+	public static enum PacketType {
+	    SERVER, CLIENT, UPGRADE_TO_SSL;
+	}
+
+	public String id = "";
+
+	public Packet() {
+	}
+
+	public Packet(String id) {
+	    this.id = id;
+	}
+
+	public PacketType type;
+
+	public boolean ignore = false;
+
+	public byte data[];
+    }
+
+    public boolean isShutdowned() {
+	return shutdowned;
+    }
+
+    public void waitUntilShutdowned(long timeToWaitMiliseconds) throws InterruptedException {
+	long deadline = System.currentTimeMillis() + timeToWaitMiliseconds;
+	while (!shutdowned && System.currentTimeMillis() < deadline) {
+	    Thread.sleep(10);
+	}
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java
new file mode 100644
index 0000000..9fe2499
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/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;
+
+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;
+    }
+  }
+
+}


[9/9] git commit: updated refs/heads/master to a98c473

Posted by de...@apache.org.
Remote Desktop Protocol(RDP) client that is suitable for including in the console VM.
The client renders RDP to a window created by Java. It is important for Hyper-V support,
because Hyper-V provides access to the consoles of VMs that it is running over RDP.


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/a98c473d
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/a98c473d
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/a98c473d

Branch: refs/heads/master
Commit: a98c473dca9ff2af1275f6c9afda773fc416a727
Parents: 57ba367
Author: Devdeep Singh <de...@gmail.com>
Authored: Fri Nov 8 13:16:44 2013 +0530
Committer: Devdeep Singh <de...@gmail.com>
Committed: Fri Nov 8 13:35:11 2013 +0530

----------------------------------------------------------------------
 .../console-proxy-rdp/rdpconsole/README.txt     |   50 +
 services/console-proxy-rdp/rdpconsole/pom.xml   |   49 +
 .../console-proxy-rdp/rdpconsole/rdp-config.bat |   42 +
 .../src/main/java/common/AwtBellAdapter.java    |   76 ++
 .../src/main/java/common/AwtCanvasAdapter.java  |  164 +++
 .../main/java/common/AwtClipboardAdapter.java   |   55 +
 .../src/main/java/common/AwtKeyEventSource.java |   53 +
 .../main/java/common/AwtMouseEventSource.java   |   71 ++
 .../src/main/java/common/BitmapOrder.java       |   48 +
 .../src/main/java/common/BitmapRectangle.java   |   74 ++
 .../main/java/common/BufferedImageCanvas.java   |   79 ++
 .../common/BufferedImageCopyRectAdapter.java    |  124 ++
 .../java/common/BufferedImagePixelsAdapter.java |  147 +++
 .../rdpconsole/src/main/java/common/Client.java |  137 +++
 .../src/main/java/common/CopyRectOrder.java     |   39 +
 .../src/main/java/common/KeyOrder.java          |   45 +
 .../src/main/java/common/MouseOrder.java        |   41 +
 .../src/main/java/common/OrderType.java         |   22 +
 .../src/main/java/common/ScreenDescription.java |  168 +++
 .../main/java/common/SizeChangeListener.java    |   22 +
 .../java/rdpclient/AwtRdpKeyboardAdapter.java   |  351 ++++++
 .../main/java/rdpclient/AwtRdpMouseAdapter.java |  180 +++
 .../java/rdpclient/ClientConfirmActivePDU.java  | 1132 ++++++++++++++++++
 .../main/java/rdpclient/ClientFastPathPDU.java  |   55 +
 .../src/main/java/rdpclient/ClientInfoPDU.java  |  456 +++++++
 .../rdpclient/ClientMCSAttachUserRequest.java   |  102 ++
 ...JoinRequest_ServerMCSChannelConfirmPDUs.java |  222 ++++
 .../java/rdpclient/ClientMCSConnectInitial.java |  669 +++++++++++
 .../rdpclient/ClientMCSErectDomainRequest.java  |  190 +++
 .../java/rdpclient/ClientPacketSniffer.java     |   50 +
 .../java/rdpclient/ClientSynchronizePDU.java    |  248 ++++
 .../src/main/java/rdpclient/ClientTpkt.java     |   54 +
 .../ClientX224ConnectionRequestPDU.java         |  156 +++
 .../main/java/rdpclient/ClientX224DataPdu.java  |   54 +
 .../src/main/java/rdpclient/HandshakeEnd.java   |   27 +
 .../src/main/java/rdpclient/MockServer.java     |  197 +++
 .../src/main/java/rdpclient/PacketSniffer.java  |   75 ++
 .../java/rdpclient/RLEBitmapDecompression.java  | 1014 ++++++++++++++++
 .../src/main/java/rdpclient/RdpClient.java      |  214 ++++
 .../src/main/java/rdpclient/RdpConstants.java   |   74 ++
 .../src/main/java/rdpclient/RdpState.java       |   33 +
 .../main/java/rdpclient/ServerBitmapUpdate.java |  201 ++++
 .../java/rdpclient/ServerChannel1003Router.java |  530 ++++++++
 .../rdpclient/ServerControlPDUCooperate.java    |  117 ++
 .../ServerControlPDUGrantedControl.java         |  114 ++
 .../java/rdpclient/ServerDemandActivePDU.java   |  661 ++++++++++
 .../src/main/java/rdpclient/ServerFastPath.java |  259 ++++
 .../ServerLicenseErrorPDUValidClient.java       |  121 ++
 .../ServerMCSAttachUserConfirmPDU.java          |  133 ++
 .../ServerMCSChannelJoinConfirmPDU.java         |   89 ++
 .../rdpclient/ServerMCSConnectResponse.java     |  283 +++++
 .../src/main/java/rdpclient/ServerMCSPDU.java   |  149 +++
 .../java/rdpclient/ServerPacketSniffer.java     |   52 +
 .../java/rdpclient/ServerPaletteUpdate.java     |   78 ++
 .../java/rdpclient/ServerSynchronizePDU.java    |  115 ++
 .../src/main/java/rdpclient/ServerTpkt.java     |   70 ++
 .../ServerX224ConnectionConfirmPDU.java         |  237 ++++
 .../main/java/rdpclient/ServerX224DataPdu.java  |   64 +
 .../rdpclient/TrustAllX509TrustManager.java     |   40 +
 .../main/java/rdpclient/UpgradeSocketToSSL.java |   44 +
 .../main/java/streamer/AssertingByteBuffer.java |  107 ++
 .../src/main/java/streamer/BaseElement.java     |  417 +++++++
 .../src/main/java/streamer/BufferPool.java      |   36 +
 .../src/main/java/streamer/ByteBuffer.java      |  826 +++++++++++++
 .../src/main/java/streamer/DataSink.java        |   24 +
 .../src/main/java/streamer/DataSource.java      |   60 +
 .../src/main/java/streamer/Direction.java       |   21 +
 .../src/main/java/streamer/Element.java         |  120 ++
 .../src/main/java/streamer/Event.java           |   33 +
 .../src/main/java/streamer/FakeSink.java        |   69 ++
 .../src/main/java/streamer/FakeSource.java      |  125 ++
 .../main/java/streamer/InputStreamSource.java   |  194 +++
 .../rdpconsole/src/main/java/streamer/Link.java |   66 +
 .../src/main/java/streamer/MockSink.java        |  111 ++
 .../src/main/java/streamer/MockSource.java      |   88 ++
 .../src/main/java/streamer/OneTimeSwitch.java   |  133 ++
 .../src/main/java/streamer/Order.java           |   23 +
 .../main/java/streamer/OutputStreamSink.java    |  153 +++
 .../src/main/java/streamer/Pipeline.java        |   91 ++
 .../src/main/java/streamer/PipelineImpl.java    |  309 +++++
 .../src/main/java/streamer/Queue.java           |  136 +++
 .../src/main/java/streamer/SocketWrapper.java   |  239 ++++
 .../src/main/java/streamer/SyncLink.java        |  402 +++++++
 .../vncclient/AwtKeyboardEventToVncAdapter.java |  369 ++++++
 .../vncclient/AwtMouseEventToVncAdapter.java    |   71 ++
 .../main/java/vncclient/EncodingsMessage.java   |   63 +
 .../vncclient/FrameBufferUpdateRequest.java     |  127 ++
 .../vncclient/RGB888LE32PixelFormatRequest.java |   90 ++
 .../src/main/java/vncclient/RfbConstants.java   |   85 ++
 .../src/main/java/vncclient/VncClient.java      |  107 ++
 .../src/main/java/vncclient/VncInitializer.java |  244 ++++
 .../main/java/vncclient/VncMessageHandler.java  |  419 +++++++
 .../java/vncclient/Vnc_3_3_Authentication.java  |  291 +++++
 .../src/main/java/vncclient/Vnc_3_3_Hello.java  |  115 ++
 .../rdpconsole/src/test/doc/README.txt          |   32 +
 .../rdpconsole/src/test/doc/dev-rdp-config.bat  |  126 ++
 .../rdpconsole/src/test/doc/rdp-key.pem         |   23 +
 .../src/test/java/rdpclient/MockServerTest.java |  189 +++
 98 files changed, 16550 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/README.txt
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/README.txt b/services/console-proxy-rdp/rdpconsole/README.txt
new file mode 100644
index 0000000..15029d4
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/README.txt
@@ -0,0 +1,50 @@
+// 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.
+This project contains code for basic VNC and RDP clients.
+
+VNC client can be invoked using following command:
+
+  mvn exec:java -Dexec.mainClass="common.Client" -Dexec.args="vnc 192.168.0.101 5901 password"
+
+where
+  * vnc - name of protcol;
+  * 192.168.0.101 - IP of VNC server;
+  * 5901 - port of VNC server screen (5900+display number);
+  * password - VNC server password.
+
+
+RDP client can be invoked using following command:
+
+  mvn exec:java -Dexec.mainClass="common.Client" -Dexec.args="rdp 192.168.0.101 3389 Administrator"
+
+where
+  * rdp - name of protcol;
+  * 192.168.0.101 - IP of RDP server;
+  * 3389 - port of RDP server;
+  * Administrator - user name for loging dialog.
+
+
+Limitations of VNC client:
+  * only basic functionality work.
+
+Limitations of RDP client:
+  * it uses SSL/TLS;
+  * NLA is not supported;
+  * only basic functionality work.
+
+
+To configure and start RDP service properly, run rdp-config.bat on server.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/pom.xml
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/pom.xml b/services/console-proxy-rdp/rdpconsole/pom.xml
new file mode 100644
index 0000000..d1b4918
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/pom.xml
@@ -0,0 +1,49 @@
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>rdpclient</groupId>
+  <artifactId>rdpclient</artifactId>
+  <version>4.3.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>rdpclient</name>
+  <url>http://maven.apache.org</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tomcat.embed</groupId>
+      <artifactId>tomcat-embed-core</artifactId>
+      <version>7.0.30</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/rdp-config.bat
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/rdp-config.bat b/services/console-proxy-rdp/rdpconsole/rdp-config.bat
new file mode 100644
index 0000000..e951014
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/rdp-config.bat
@@ -0,0 +1,42 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem 
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem 
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+
+rem 
+rem Configure and start RDP service.
+rem 
+ 
+rem Turn off firewall
+
+netsh advfirewall firewall set rule group="Remote Desktop" new enable=yes
+
+rem Enable TS connections
+
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "AllowTSConnections" /t REG_DWORD /d 1 /f
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "fDenyTSConnections" /t REG_DWORD /d 0 /f
+
+rem Disable RDP NLA
+
+reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0 /f
+
+rem Enable TS service
+
+sc config TermService start=auto
+
+rem Start TS service
+
+net start Termservice
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java
new file mode 100644
index 0000000..3318c09
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java
@@ -0,0 +1,76 @@
+// 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 common;
+
+import java.awt.Toolkit;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.FakeSource;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class AwtBellAdapter extends BaseElement {
+
+  public AwtBellAdapter(String id) {
+    super(id);
+    declarePads();
+  }
+
+  private void declarePads() {
+    inputPads.put(STDIN, null);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    if (buf == null)
+      return;
+
+    Toolkit.getDefaultToolkit().beep();
+  }
+
+  public String toString() {
+    return "Bell(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    System.setProperty("streamer.Element.debug", "true");
+
+    Element source = new FakeSource("source") {
+      {
+        this.incommingBufLength = 0;
+        this.delay = 1000;
+        this.numBuffers = 3;
+      }
+    };
+
+    Element sink = new AwtBellAdapter("sink");
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.addAndLink(source, sink);
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java
new file mode 100644
index 0000000..12dd88b
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java
@@ -0,0 +1,164 @@
+// 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 common;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+
+import rdpclient.ServerBitmapUpdate;
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Order;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class AwtCanvasAdapter extends BaseElement {
+
+  protected ScreenDescription screen;
+
+  public AwtCanvasAdapter(String id, BufferedImageCanvas canvas, ScreenDescription screen) {
+    super(id);
+    this.canvas = canvas;
+    this.screen = screen;
+  }
+
+  protected BufferedImageCanvas canvas;
+
+  public String toString() {
+    return "AwtRdpAdapter(" + id + ")";
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    Order order = (Order) buf.getOrder();
+    switch ((OrderType) order.type) {
+
+    case BITMAP_UPDATE:
+      handleBitmap((BitmapOrder) order, buf);
+      break;
+
+    case COPY_RECT:
+      handleCopyRect((CopyRectOrder) order, buf);
+      break;
+
+    default:
+      throw new RuntimeException("Order is not implemented: " + buf + ".");
+      // break;
+    }
+
+    buf.unref();
+  }
+
+  private void handleCopyRect(CopyRectOrder order, ByteBuffer buf) {
+    // TODO Auto-generated method stub
+    // Copy image
+    canvas.getOfflineGraphics().copyArea(order.srcX, order.srcY, order.width, order.height, order.x - order.srcX, order.y - order.srcY);
+
+    // Request update of repainted area
+    canvas.repaint(order.x, order.y, order.width, order.height);
+
+  }
+
+  private void handleBitmap(BitmapOrder order, ByteBuffer buf) {
+    // Draw rectangle on offline buffer
+    BufferedImage image = canvas.getOfflineImage();
+    Graphics2D g = (Graphics2D) image.getGraphics();
+
+    for (BitmapRectangle rectangle : order.rectangles) {
+      // *DEBUG*/System.out.println("["+this+"] DEBUG: Rectangle: " +
+      // rectangle.toString());
+
+      int x = rectangle.x;
+      int y = rectangle.y;
+      int width = rectangle.width;
+      int height = rectangle.height;
+      int bufferWidth = rectangle.bufferWidth;
+      int bufferHeight = rectangle.bufferHeight;
+
+      BufferedImage rectImage;
+      switch (rectangle.colorDepth) {
+      case 8: {
+        rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_BYTE_INDEXED, screen.colorMap);
+        WritableRaster raster = rectImage.getRaster();
+        raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toByteArray());
+        break;
+      }
+      case 15: {
+        rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_555_RGB);
+        WritableRaster raster = rectImage.getRaster();
+        raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray());
+        break;
+      }
+      case 16: {
+        rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_565_RGB);
+        WritableRaster raster = rectImage.getRaster();
+        raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray());
+        break;
+      }
+      case 24:
+      case 32: {
+        rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_INT_RGB);
+        WritableRaster raster = rectImage.getRaster();
+        raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toIntLEArray());
+        break;
+      }
+      default:
+        throw new RuntimeException("Unsupported color depth: " + rectangle.colorDepth + ".");
+      }
+
+      g.setClip(x, y, width, height);
+      g.drawImage(rectImage, x, y, null);
+
+      // Request update of repainted area
+      canvas.repaint(x, y, width, height);
+    }
+
+  }
+
+  /**
+   * 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");
+    ByteBuffer packet = new ByteBuffer(new byte[] { 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00,
+        0x01, 0x04, 0x0a, 0x00, 0x0c, (byte) 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+
+    Pipeline pipeline = new PipelineImpl("test");
+
+    Element bitmap = new ServerBitmapUpdate("bitmap");
+
+    BufferedImageCanvas canvas = new BufferedImageCanvas(1024, 768);
+    Element adapter = new AwtCanvasAdapter("test", canvas, null) {
+      {
+        verbose = true;
+      }
+    };
+    pipeline.addAndLink(bitmap, adapter);
+
+    bitmap.handleData(packet, null);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java
new file mode 100644
index 0000000..aeed41c
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java
@@ -0,0 +1,55 @@
+// 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 common;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.StringSelection;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+import vncclient.VncMessageHandler;
+
+public class AwtClipboardAdapter extends BaseElement {
+
+  public AwtClipboardAdapter(String id) {
+    super(id);
+    declarePads();
+  }
+
+  private void declarePads() {
+    inputPads.put(STDIN, null);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    if (buf == null)
+      return;
+
+    String content = (String) buf.getMetadata(VncMessageHandler.CLIPBOARD_CONTENT);
+    StringSelection contents = new StringSelection(content);
+    Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null);
+  }
+
+  public String toString() {
+    return "Clipboard(" + id + ")";
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java
new file mode 100644
index 0000000..a754796
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java
@@ -0,0 +1,53 @@
+// 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 common;
+
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+
+public class AwtKeyEventSource extends BaseElement implements KeyListener {
+
+  public AwtKeyEventSource(String id) {
+    super(id);
+  }
+
+  @Override
+  public void keyTyped(KeyEvent e) {
+    // Nothing to do
+
+  }
+
+  @Override
+  public void keyPressed(KeyEvent e) {
+    sendEvent(e, true);
+  }
+
+  @Override
+  public void keyReleased(KeyEvent e) {
+    sendEvent(e, false);
+  }
+
+  private void sendEvent(KeyEvent e, boolean pressed) {
+    ByteBuffer buf=new ByteBuffer(new KeyOrder(e, pressed));
+
+    pushDataToAllOuts(buf);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.java
new file mode 100644
index 0000000..f3e58fc
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.java
@@ -0,0 +1,71 @@
+// 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 common;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+
+public class AwtMouseEventSource extends BaseElement implements MouseListener, MouseMotionListener {
+
+  public AwtMouseEventSource(String id) {
+    super(id);
+  }
+
+  @Override
+  public void mouseClicked(MouseEvent e) {
+    // Nothing to do
+  }
+
+  @Override
+  public void mousePressed(MouseEvent e) {
+    MouseOrder order = new MouseOrder(e);
+    order.pressed = true;
+    pushDataToAllOuts(new ByteBuffer(order));
+  }
+
+  @Override
+  public void mouseReleased(MouseEvent e) {
+    MouseOrder order = new MouseOrder(e);
+    order.released = true;
+    pushDataToAllOuts(new ByteBuffer(order));
+  }
+
+  @Override
+  public void mouseEntered(MouseEvent e) {
+    // Nothing to do
+  }
+
+  @Override
+  public void mouseExited(MouseEvent e) {
+    // Nothing to do
+  }
+
+  @Override
+  public void mouseDragged(MouseEvent e) {
+    pushDataToAllOuts(new ByteBuffer(new MouseOrder(e)));
+  }
+
+  @Override
+  public void mouseMoved(MouseEvent e) {
+    pushDataToAllOuts(new ByteBuffer(new MouseOrder(e)));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java
new file mode 100644
index 0000000..438755f
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java
@@ -0,0 +1,48 @@
+// 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 common;
+
+import java.util.Arrays;
+
+
+import streamer.Order;
+
+/**
+ * Not an order, but derived from Order class for compatibility with orders.
+ * 
+ * @see http://msdn.microsoft.com/en-us/library/dd306368.aspx
+ */
+public class BitmapOrder extends Order {
+
+  public BitmapOrder() {
+    type = OrderType.BITMAP_UPDATE;
+  }
+
+  /**
+   * Structures, each of which contains a rectangular clipping taken from the
+   * server-side screen frame buffer.
+   */
+  public BitmapRectangle rectangles[];
+
+  @Override
+  public String toString() {
+    final int maxLen = 10;
+    return String.format("BitmapUpdateOrder [rectangles=%s]", rectangles != null ? Arrays.asList(rectangles).subList(0, Math.min(rectangles.length, maxLen))
+        : null);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java
new file mode 100644
index 0000000..f0c178e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java
@@ -0,0 +1,74 @@
+// 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 common;
+
+import streamer.ByteBuffer;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240612.aspx
+ */
+public class BitmapRectangle {
+
+  /**
+   * Left bound of the rectangle.
+   */
+  public int x;
+
+  /**
+   * Top bound of the rectangle.
+   */
+  public int y;
+
+  /**
+   * Width of the rectangle.
+   */
+  public int width;
+
+  /**
+   * Height of the rectangle.
+   */
+  public int height;
+
+  /**
+   * Color depth of the rectangle data in bits-per-pixel.
+   */
+  public int colorDepth;
+
+  /**
+   * Variable-length array of bytes describing a raw uncompressed bitmap image.
+   */
+  public ByteBuffer bitmapDataStream;
+
+  /**
+   * Size of single horizontal scan line.
+   */
+  public int bufferWidth;
+
+  /**
+   * Number of horizontal scan lines in buffer.
+   */
+  public int bufferHeight;
+
+  @Override
+  public String toString() {
+    return String
+        .format(
+            "BitmapUpdateRectangle [x=%s, y=%s, width=%s, height=%s, bitsPerPixel=%s, bitmapDataStream=%s]",
+            x, y, width, height, colorDepth, bitmapDataStream);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java
new file mode 100644
index 0000000..6e7738f
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java
@@ -0,0 +1,79 @@
+// 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 common;
+
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * A <code>BuffereImageCanvas</code> component represents frame buffer image on the
+ * screen. It also notifies its subscribers when screen is repainted.
+ */
+public class BufferedImageCanvas extends Canvas {
+  private static final long serialVersionUID = 1L;
+
+   // Offline screen buffer
+  private BufferedImage offlineImage;
+  
+  // Cached Graphics2D object for offline screen buffer
+  private Graphics2D graphics;
+
+  public BufferedImageCanvas(int width, int height) {
+    super();
+
+    setBackground(Color.black);
+    
+    setFocusable(true);
+
+    // Don't intercept TAB key
+    setFocusTraversalKeysEnabled(false);
+
+    setCanvasSize(width, height);
+  }
+
+  public void setCanvasSize(int width, int height) {
+    this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+    graphics = offlineImage.createGraphics();
+
+    setSize(offlineImage.getWidth(), offlineImage.getHeight());
+  }
+
+  @Override
+  public void update(Graphics g) {
+    // Call paint() directly, without clearing screen first
+    paint(g);
+  }
+
+  @Override
+  public void paint(Graphics g) {
+    // Only part of image, requested with repaint(Rectangle), will be
+    // painted on screen.
+    g.drawImage(offlineImage, 0, 0, this);
+  }
+
+  public BufferedImage getOfflineImage() {
+    return offlineImage;
+  }
+
+  public Graphics2D getOfflineGraphics() {
+    return graphics;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java
new file mode 100644
index 0000000..bbd5142
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java
@@ -0,0 +1,124 @@
+// 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 common;
+
+import java.awt.image.DataBufferInt;
+import java.util.Arrays;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+
+public class BufferedImageCopyRectAdapter extends BaseElement {
+
+  public static final String SRC_X = "srcX";
+  public static final String SRC_Y = "srcY";
+  public static final String TARGET_X = "x";
+  public static final String TARGET_Y = "y";
+  public static final String WIDTH = "width";
+  public static final String HEIGHT = "height";
+
+  protected BufferedImageCanvas canvas;
+
+  public BufferedImageCopyRectAdapter(String id, BufferedImageCanvas canvas) {
+    super(id);
+    this.canvas = canvas;
+    declarePads();
+  }
+
+  private void declarePads() {
+    inputPads.put(STDIN, null);
+  }
+
+  public String toString() {
+    return "Renderer(" + id + ")";
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+    
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    int x = (Integer) buf.getMetadata(TARGET_X);
+    int y = (Integer) buf.getMetadata(TARGET_Y);
+    int width = (Integer) buf.getMetadata(WIDTH);
+    int height = (Integer) buf.getMetadata(HEIGHT);
+    int srcX = (Integer) buf.getMetadata(SRC_X);
+    int srcY = (Integer) buf.getMetadata(SRC_Y);
+    buf.unref();
+
+    // Copy image
+    canvas.getOfflineGraphics().copyArea(srcX, srcY, width, height, x - srcX, y - srcY);
+
+    // Request update of repainted area
+    canvas.repaint(x, y, width, height);
+  }
+
+  public static void main(String args[]) {
+    System.setProperty("streamer.Element.debug", "true");
+
+    BufferedImageCanvas canvas = new BufferedImageCanvas(4, 4);
+
+    Element renderer = new BufferedImageCopyRectAdapter("renderer", canvas);
+
+    int[] pixelsBeforeCopy = new int[] {
+        // 0
+        1, 2, 3, 4,
+        // 1
+        5, 6, 7, 8,
+        // 2
+        9, 10, 11, 12,
+        // 3
+        13, 14, 15, 16 };
+    int[] pixelsAfterCopy = new int[] {
+        // 0
+        11, 12, 3, 4,
+        // 1
+        15, 16, 7, 8,
+        // 2
+        9, 10, 11, 12,
+        // 3
+        13, 14, 15, 16 };
+
+    
+    // Initalize image
+    int[] data = ((DataBufferInt) canvas.getOfflineImage().getRaster().getDataBuffer()).getData();
+    System.arraycopy(pixelsBeforeCopy, 0, data, 0, pixelsBeforeCopy.length);
+    
+    ByteBuffer buf = new ByteBuffer(new byte[0]);
+    buf.putMetadata(TARGET_X, 0);
+    buf.putMetadata(TARGET_Y, 0);
+    buf.putMetadata(WIDTH, 2);
+    buf.putMetadata(HEIGHT, 2);
+    buf.putMetadata(SRC_X, 2);
+    buf.putMetadata(SRC_Y, 2);
+
+    renderer.handleData(buf, null);
+
+    data = ((DataBufferInt) canvas.getOfflineImage().getRaster().getDataBuffer()).getData();
+    String actualData = Arrays.toString(data);
+    String expectedData = Arrays.toString(pixelsAfterCopy);
+    if (!actualData.equals(expectedData))
+      System.err.println("Actual image:   " + actualData + "\nExpected image: " + expectedData + ".");
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java
new file mode 100644
index 0000000..95e7738
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java
@@ -0,0 +1,147 @@
+// 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 common;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferInt;
+import java.util.Arrays;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+
+public class BufferedImagePixelsAdapter extends BaseElement {
+
+  public static final String TARGET_X = "x";
+  public static final String TARGET_Y = "y";
+  public static final String WIDTH = "width";
+  public static final String HEIGHT = "height";
+  public static final String RGB888LE32 = "RGB888LE32";
+  public static final String PIXEL_FORMAT = "pixel_format";
+
+  protected BufferedImageCanvas canvas;
+
+  public BufferedImagePixelsAdapter(String id, BufferedImageCanvas canvas) {
+    super(id);
+    this.canvas = canvas;
+    declarePads();
+  }
+
+  private void declarePads() {
+    inputPads.put(STDIN, null);
+  }
+
+  public String toString() {
+    return "Renderer(" + id + ")";
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    int x = (Integer) buf.getMetadata(TARGET_X);
+    int y = (Integer) buf.getMetadata(TARGET_Y);
+    int rectWidth = (Integer) buf.getMetadata(WIDTH);
+    int rectHeight = (Integer) buf.getMetadata(HEIGHT);
+    String format = (String) buf.getMetadata(PIXEL_FORMAT);
+
+    int bpp;
+    // Support RGB888/32 little endian only
+    if (format != null && RGB888LE32.equals(format)) {
+      bpp = 4;
+      // TODO: support more formats
+    } else
+      throw new RuntimeException("Unsupported format: " + format + ". Supported formats: " + RGB888LE32 + ".");
+
+    int dataLength = rectWidth * rectHeight * bpp;
+    if (!cap(buf, dataLength, dataLength, link, false))
+      return;
+
+    // Draw rectangle on offline buffer
+    BufferedImage image = canvas.getOfflineImage();
+
+    DataBuffer dataBuf = image.getRaster().getDataBuffer();
+
+    switch (dataBuf.getDataType()) {
+
+    case DataBuffer.TYPE_INT: {
+
+      // Convert array of bytes to array of int's
+      int[] intArray = buf.toIntLEArray();
+
+      // We chose RGB888 model, so Raster will use DataBufferInt type
+      DataBufferInt dataBuffer = (DataBufferInt) dataBuf;
+
+      int imageWidth = image.getWidth();
+      int imageHeight = image.getHeight();
+
+      // Paint rectangle directly on buffer, line by line
+      int[] imageBuffer = dataBuffer.getData();
+
+      for (int srcLine = 0, dstLine = y; srcLine < rectHeight && dstLine < imageHeight; srcLine++, dstLine++) {
+        try {
+          System.arraycopy(intArray, srcLine * rectWidth, imageBuffer, x + dstLine * imageWidth, rectWidth);
+        } catch (IndexOutOfBoundsException e) {
+        }
+      }
+      break;
+    }
+
+    default:
+      throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: "
+          + dataBuf.getClass().getSimpleName());
+    }
+
+    // Request update of repainted area
+    canvas.repaint(x, y, rectWidth, rectHeight);
+
+    buf.unref();
+  }
+
+  public static void main(String args[]) {
+    System.setProperty("streamer.Element.debug", "true");
+
+    BufferedImageCanvas canvas = new BufferedImageCanvas(4, 4);
+
+    Element renderer = new BufferedImagePixelsAdapter("renderer", canvas);
+
+    byte[] pixels = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5,
+        6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+    int[] pixelsLE = new int[] { 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605,
+        0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d };
+
+    ByteBuffer buf = new ByteBuffer(pixels);
+    buf.putMetadata(TARGET_X, 0);
+    buf.putMetadata(TARGET_Y, 0);
+    buf.putMetadata(WIDTH, 4);
+    buf.putMetadata(HEIGHT, 4);
+    buf.putMetadata(PIXEL_FORMAT, RGB888LE32);
+
+    renderer.handleData(buf, null);
+
+    String actualData = Arrays.toString(((DataBufferInt) canvas.getOfflineImage().getRaster().getDataBuffer()).getData());
+    String expectedData = Arrays.toString(pixelsLE);
+    if (!actualData.equals(expectedData))
+      System.err.println("Actual image:   " + actualData + "\nExpected image: " + expectedData + ".");
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java
new file mode 100644
index 0000000..343fe0a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java
@@ -0,0 +1,137 @@
+// 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 common;
+
+import java.awt.Frame;
+import java.awt.ScrollPane;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.net.InetSocketAddress;
+
+import rdpclient.RdpClient;
+import streamer.Element;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.SocketWrapper;
+import vncclient.VncClient;
+
+public class Client {
+
+  private static Frame frame;
+  private static SocketWrapper socket;
+  private static ScrollPane scroller;
+  private static ScreenDescription screen;
+  private static BufferedImageCanvas canvas;
+
+  public static void main(String args[]) {
+    // System.setProperty("streamer.Link.debug", "true");
+    // System.setProperty("streamer.Element.debug", "true");
+    // System.setProperty("streamer.Pipeline.debug", "true");
+
+    try {
+      if (args.length < 4) {
+        System.out.println("Usage: \n  java common.Client vnc IP PORT PASSWORD\n  java common.Client rdp IP PORT username\n");
+        System.exit(0);
+      }
+
+      String connectionType = args[0];
+      String hostname = args[1];
+      int port = Integer.parseInt(args[2]);
+      String userNameOrPassword = args[3];
+
+      // Create address from arguments
+      InetSocketAddress address = new InetSocketAddress(hostname, port);
+
+      // Create socket wrapper
+      socket = new SocketWrapper("socket");
+
+      screen = new ScreenDescription();
+      canvas = new BufferedImageCanvas(1024, 768);
+      screen.addSizeChangeListener(new SizeChangeListener() {
+        @Override
+        public void sizeChanged(int width, int height) {
+          if (canvas != null) {
+            canvas.setCanvasSize(width, height);
+            if (scroller != null)
+              scroller.setSize(canvas.getWidth(), canvas.getHeight());
+          }
+        }
+      });
+
+      // Assemble pipeline
+      Element main;
+      if ("vnc".equals(connectionType)) {
+        main = new VncClient("client", userNameOrPassword, screen, canvas);
+      } else if ("rdp".equals(connectionType)) {
+        main = new RdpClient("client", userNameOrPassword, screen, canvas);
+      } else {
+        throw new RuntimeException("Unknown connection type. Expected value: \"vnc\" or \"rdp\", actual value: \"" + connectionType + "\".");
+      }
+
+      Pipeline pipeline = new PipelineImpl("Client");
+      pipeline.add(socket, main);
+      pipeline.link("socket", main.getId(), "socket");
+
+      pipeline.validate();
+      
+      frame = createVncClientMainWindow(canvas, "VNC", new WindowAdapter() {
+        public void windowClosing(WindowEvent evt) {
+          shutdown();
+        }
+      });
+
+      try {
+        // Connect socket to remote server and run main loop(s)
+        socket.connect(address);
+      } finally {
+        shutdown();
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace(System.err);
+    }
+  }
+
+  protected static void shutdown() {
+    if (frame != null) {
+      frame.setVisible(false);
+      frame.dispose();
+    }
+    if (socket != null)
+      socket.shutdown();
+  }
+
+  protected static Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title, WindowListener windowListener) {
+    // Create AWT windows
+    Frame frame = new Frame(title + " - RDP");
+
+    // Use scrolling pane to support screens, which are larger than ours
+    scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
+    scroller.add(canvas);
+    scroller.setSize(canvas.getWidth(), canvas.getHeight());
+
+    frame.add(scroller);
+    frame.pack();
+    frame.setVisible(true);
+
+    frame.addWindowListener(windowListener);
+
+    return frame;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java
new file mode 100644
index 0000000..5f0c0f7
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java
@@ -0,0 +1,39 @@
+// 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 common;
+
+
+import streamer.Order;
+
+public class CopyRectOrder extends Order {
+  public int srcX;
+  public int srcY;
+  public int width;
+  public int height;
+  public int x;
+  public int y;
+
+  public CopyRectOrder() {
+    type = OrderType.COPY_RECT;
+  }
+
+  @Override
+  public String toString() {
+    return "CopyRectOrder [srcX=" + srcX + ", srcY=" + srcY + ", width=" + width + ", height=" + height + ", x=" + x + ", y=" + y + "]";
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.java
new file mode 100644
index 0000000..726d8a0
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.java
@@ -0,0 +1,45 @@
+// 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 common;
+
+import java.awt.event.KeyEvent;
+
+import streamer.Order;
+
+public class KeyOrder extends Order {
+  public KeyOrder() {
+    type = "key event";
+  }
+
+  public KeyOrder(KeyEvent event, boolean pressed) {
+    type = "key event";
+    
+    this.event = event;
+    this.pressed = pressed;
+  }
+
+  public KeyEvent event;
+  
+  public boolean pressed;
+
+  @Override
+  public String toString() {
+    return "KeyOrder [event=" + event + ", pressed=" + pressed + "]";
+  }
+  
+  
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.java
new file mode 100644
index 0000000..d6670fa
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.java
@@ -0,0 +1,41 @@
+// 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 common;
+
+import java.awt.event.MouseEvent;
+
+import streamer.Order;
+
+public class MouseOrder extends Order {
+
+  public MouseOrder() {
+    type = "mouse order";
+  }
+
+  public MouseOrder(MouseEvent event) {
+    type = "mouse order";
+    this.event = event;
+  }
+
+  public MouseEvent event;
+  public boolean pressed;
+  public boolean released;
+  @Override
+  public String toString() {
+    return "MouseOrder [event=" + event + ", pressed=" + pressed + ", released=" + released + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java
new file mode 100644
index 0000000..1e77ea9
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java
@@ -0,0 +1,22 @@
+// 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 common;
+
+public enum OrderType {
+  BITMAP_UPDATE, COPY_RECT,
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java
new file mode 100644
index 0000000..a01f22e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java
@@ -0,0 +1,168 @@
+// 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 common;
+
+import java.awt.image.IndexColorModel;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * VncScreenDescription - contains information about remote VNC screen.
+ */
+public class ScreenDescription {
+  
+  protected Set<SizeChangeListener> sizeChangeListeners = new HashSet<SizeChangeListener>();
+
+  // Frame buffer size
+  protected int framebufferWidth = -1;
+  protected int framebufferHeight = -1;
+
+  // Desktop name
+  protected String desktopName = null;
+
+  // Bytes per pixel
+  protected int bytesPerPixel;
+  protected int colorDepth;
+  protected int bitsPerPixel;
+  protected int redShift;
+  protected int greenShift;
+  protected int blueShift;
+  protected int redMax;
+  protected int greenMax;
+  protected int blueMax;
+  protected boolean bigEndianFlag;
+  protected boolean trueColorFlag;
+
+  public IndexColorModel colorMap = null;
+
+  public ScreenDescription() {
+  }
+
+  /**
+   * Store information about server pixel format.
+   */
+  public void setPixelFormat(int bitsPerPixel, int depth, boolean bigEndianFlag, boolean trueColorFlag, int redMax, int greenMax, int blueMax, int redShift,
+      int greenShift, int blueShift) {
+
+    this.bytesPerPixel = (bitsPerPixel + 7) / 8;
+
+    this.bitsPerPixel = bitsPerPixel;
+    this.colorDepth = depth;
+    this.bigEndianFlag = bigEndianFlag;
+    this.trueColorFlag = trueColorFlag;
+
+    this.redMax = redMax;
+    this.greenMax = greenMax;
+    this.blueMax = blueMax;
+    this.redShift = redShift;
+    this.greenShift = greenShift;
+    this.blueShift = blueShift;
+  }
+
+  /**
+   * Store information about server pixel format.
+   */
+  public void setPixelFormatRGBTrueColor(int bitsPerPixel) {
+
+    switch(bitsPerPixel) {
+    case 8:
+      setPixelFormat(8, 8, false, false, -1, -1, -1, -1, -1, -1);
+      break;
+    case 15:
+      setPixelFormat(16, 15, false, true, 31, 31, 31, 0, 5, 10);
+      break;
+    case 16:
+      setPixelFormat(16, 16, false, true, 31, 63, 31, 0, 5, 11);
+      break;
+    case 24:
+      setPixelFormat(24, 24, false, true, 255, 255, 255, 0, 8, 16);
+      break;
+    case 32:
+      setPixelFormat(32, 24, false, true, 255, 255, 255, 0, 8, 16);
+      break;
+    default:
+      throw new RuntimeException("Unknown color depth.");
+    }
+
+  }
+
+  /**
+   * Store information about server screen size.
+   */
+  public void setFramebufferSize(int width, int height) {
+    if (height <= 0 || width <= 0)
+      throw new RuntimeException("Incorrect framebuffer size: " + width + "x" + height + ".");
+
+    this.framebufferWidth = width;
+    this.framebufferHeight = height;
+    
+    callSizeChangeListeners(width, height);
+  }
+
+  protected void callSizeChangeListeners(int width, int height) {
+    for(SizeChangeListener listener:sizeChangeListeners) {
+      listener.sizeChanged(width, height);
+    }
+  }
+
+  /**
+   * Store server desktop name.
+   */
+  public void setDesktopName(String desktopName) {
+    this.desktopName = desktopName;
+  }
+
+  // Getters for variables, as usual
+
+  public String getDesktopName() {
+    return desktopName;
+  }
+
+  public int getBytesPerPixel() {
+    return bytesPerPixel;
+  }
+
+  public int getFramebufferHeight() {
+    return framebufferHeight;
+  }
+
+  public int getFramebufferWidth() {
+    return framebufferWidth;
+  }
+
+  public boolean isRGB888_32_LE() {
+    return (colorDepth == 24 && bitsPerPixel == 32 && redShift == 0 && greenShift == 8 && blueShift == 16 && redMax == 255 && greenMax == 255 && blueMax == 255
+        && !bigEndianFlag && trueColorFlag);
+  }
+
+  @Override
+  public String toString() {
+    return "ScreenDescription [framebufferWidth=" + framebufferWidth + ", framebufferHeight=" + framebufferHeight + ", desktopName=" + desktopName
+        + ", bytesPerPixel=" + bytesPerPixel + ", depth=" + colorDepth + ", bitsPerPixel=" + bitsPerPixel + ", redShift=" + redShift + ", greenShift=" + greenShift
+        + ", blueShift=" + blueShift + ", redMax=" + redMax + ", greenMax=" + greenMax + ", blueMax=" + blueMax + ", bigEndianFlag=" + bigEndianFlag
+        + ", trueColorFlag=" + trueColorFlag + "]";
+  }
+
+  public void addSizeChangeListener(SizeChangeListener sizeChangeListener) {
+    sizeChangeListeners.add(sizeChangeListener);
+  }
+
+  public int getColorDeph() {
+    return colorDepth;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
new file mode 100644
index 0000000..d658d1b
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
@@ -0,0 +1,22 @@
+// 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 common;
+
+public interface SizeChangeListener {
+
+  public void sizeChanged(int width, int height);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java
new file mode 100644
index 0000000..f19f09c
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java
@@ -0,0 +1,351 @@
+// 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.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/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java
new file mode 100644
index 0000000..371bffc
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.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;
+
+import java.awt.event.MouseEvent;
+
+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 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 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 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 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 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 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 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 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 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 & MouseEvent.BUTTON1_DOWN_MASK) > 0) && !button1;
+      boolean b2 = ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) > 0) && !button2;
+      boolean b3 = ((modifiers & MouseEvent.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 & MouseEvent.BUTTON1_DOWN_MASK) > 0) && button1;
+      boolean b2 = !((modifiers & MouseEvent.BUTTON2_DOWN_MASK) > 0) && button2;
+      boolean b3 = !((modifiers & MouseEvent.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;
+  }
+
+}


[2/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java
new file mode 100644
index 0000000..2ddf0b6
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java
@@ -0,0 +1,239 @@
+// 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 streamer;
+
+import static rdpclient.MockServer.Packet.PacketType.CLIENT;
+import static rdpclient.MockServer.Packet.PacketType.SERVER;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.HashMap;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import rdpclient.MockServer;
+import rdpclient.MockServer.Packet;
+import rdpclient.TrustAllX509TrustManager;
+
+public class SocketWrapper extends PipelineImpl {
+
+  protected InputStreamSource source;
+  protected OutputStreamSink sink;
+  protected Socket socket;
+  protected InetSocketAddress address;
+
+  protected SSLSocket sslSocket;
+
+  //protected String SSL_VERSION_TO_USE = "TLSv1.2";
+  /*DEBUG*/protected String SSL_VERSION_TO_USE = "TLSv1";
+
+  public SocketWrapper(String id) {
+    super(id);
+  }
+
+  @Override
+  protected HashMap<String, Element> initElementMap(String id) {
+    HashMap<String, Element> map = new HashMap<String, Element>();
+
+    source = new InputStreamSource(id + "." + OUT, this);
+    sink = new OutputStreamSink(id + "." + IN, this);
+
+    // Pass requests to read data to socket input stream
+    map.put(OUT, source);
+
+    // All incoming data, which is sent to this socket wrapper, will be sent
+    // to socket remote
+    map.put(IN, sink);
+
+    return map;
+  }
+
+  /**
+   * Connect this socket wrapper to remote server and start main loop on
+   * IputStreamSource stdout link, to watch for incoming data, and
+   * OutputStreamSink stdin link, to pull for outgoing data.
+   * 
+   * @param address
+   * @throws IOException
+   */
+  public void connect(InetSocketAddress address) throws IOException {
+    this.address = address;
+
+    // Connect socket to server
+    socket = SocketFactory.getDefault().createSocket();
+    try {
+      socket.connect(address);
+
+      InputStream is = socket.getInputStream();
+      source.setInputStream(is);
+
+      OutputStream os = socket.getOutputStream();
+      sink.setOutputStream(os);
+
+      // Start polling for data to send to remote sever
+      runMainLoop(IN, STDIN, true, true);
+
+      // Push incoming data from server to handlers
+      runMainLoop(OUT, STDOUT, false, false);
+
+    } finally {
+      socket.close();
+    }
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    switch (event) {
+    case SOCKET_UPGRADE_TO_SSL:
+      upgradeToSsl();
+      break;
+    default:
+      super.handleEvent(event, direction);
+      break;
+    }
+  }
+
+  public void upgradeToSsl() {
+
+    if (sslSocket != null)
+      // Already upgraded
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Upgrading socket to SSL.");
+
+    try {
+      // Use most secure implementation of SSL available now.
+      // JVM will try to negotiate TLS1.2, then will fallback to TLS1.0, if
+      // TLS1.2 is not supported.
+      SSLContext sslContext = SSLContext.getInstance(SSL_VERSION_TO_USE);
+
+      // Trust all certificates (FIXME: insecure)
+      sslContext.init(null, new TrustManager[] { new TrustAllX509TrustManager() }, null);
+
+      SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+      sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, address.getHostString(), address.getPort(), true);
+      sslSocket.startHandshake();
+
+      InputStream sis = sslSocket.getInputStream();
+      source.setInputStream(sis);
+
+      OutputStream sos = sslSocket.getOutputStream();
+      sink.setOutputStream(sos);
+
+    } catch (Exception e) {
+      throw new RuntimeException("Cannot upgrade socket to SSL: " + e.getMessage(), e);
+    }
+
+  }
+
+  @Override
+  public void validate() {
+    for (Element element : elements.values())
+      element.validate();
+
+    if (get(IN).getPads(Direction.IN).size() == 0)
+      throw new RuntimeException("[ " + this + "] Input of socket is not connected.");
+
+    if (get(OUT).getPads(Direction.OUT).size() == 0)
+      throw new RuntimeException("[ " + this + "] Output of socket is not connected.");
+
+  }
+
+  public void shutdown() {
+    try {
+      handleEvent(Event.STREAM_CLOSE, Direction.IN);
+    } catch (Exception e) {
+    }
+    try {
+      handleEvent(Event.STREAM_CLOSE, Direction.OUT);
+    } catch (Exception e) {
+    }
+    try {
+      if (sslSocket != null)
+        sslSocket.close();
+    } catch (Exception e) {
+    }
+    try {
+      socket.close();
+    } catch (Exception e) {
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "SocketWrapper(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    try {
+      System.setProperty("streamer.Link.debug", "true");
+      System.setProperty("streamer.Element.debug", "true");
+      System.setProperty("rdpclient.MockServer.debug", "true");
+
+      Pipeline pipeline = new PipelineImpl("echo client");
+
+      SocketWrapper socketWrapper = new SocketWrapper("socket");
+
+      pipeline.add(socketWrapper);
+      pipeline.add(new BaseElement("echo"));
+
+      pipeline.link("socket", "echo", "socket");
+
+      final byte[] mockData = new byte[] { 0x01, 0x02, 0x03 };
+      MockServer server = new MockServer(new Packet[] { new Packet("Server hello") {
+        {
+          type = SERVER;
+          data = mockData;
+        }
+      }, new Packet("Client hello") {
+        {
+          type = CLIENT;
+          data = mockData;
+        }
+      }, new Packet("Server hello") {
+        {
+          type = SERVER;
+          data = mockData;
+        }
+      }, new Packet("Client hello") {
+        {
+          type = CLIENT;
+          data = mockData;
+        }
+      } });
+      server.start();
+      InetSocketAddress address = server.getAddress();
+
+      socketWrapper.connect(address);
+
+    } catch (IOException e) {
+      e.printStackTrace(System.err);
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java
new file mode 100644
index 0000000..32c14bb
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java
@@ -0,0 +1,402 @@
+// 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 streamer;
+
+/**
+ * Link to transfer data in bounds of single thread (synchronized transfer).
+ * Must not be used to send data to elements served in different threads.
+ */
+public class SyncLink implements Link {
+
+  /**
+   * When null packet is pulled from source element, then make slight delay to
+   * avoid consuming of 100% of CPU in main loop in cases when link is pauses or
+   * source element cannot produce data right now.
+   */
+  protected static final long STANDARD_DELAY_FOR_EMPTY_PACKET = 10; // Milliseconds
+
+  /**
+   * Delay for null packets in poll method when blocking is requested, in
+   * milliseconds.
+   */
+  protected long delay = STANDARD_DELAY_FOR_EMPTY_PACKET;
+
+  /**
+   * Set to true to print debugging messages.
+   */
+  protected boolean verbose = System.getProperty("streamer.Link.debug", "false").equals("true");;
+
+  /**
+   * ID of this link.
+   */
+  protected String id = null;
+
+  /**
+   * Buffer with data to hold because link is paused, or data is pushed back.
+   */
+  protected ByteBuffer cacheBuffer = null;
+
+  /**
+   * Size of expected packet. Data must be hold in link until full packet will
+   * be read.
+   */
+  protected int expectedPacketSize = 0;
+
+  /**
+   * Number of packets and packet header transferred to element.
+   */
+  protected int packetNumber = 0;
+
+  /**
+   * Set to true to hold all data in link until it will be set to false again.
+   */
+  protected boolean paused = false;
+
+  /**
+   * Element to pull data from, when in pull mode.
+   */
+  protected Element source = null;
+
+  /**
+   * Element to send data to in both pull and push modes.
+   */
+  protected Element sink = null;
+
+  /**
+   * When in loop, indicates that loop must be stopped.
+   * 
+   * @see run()
+   */
+  private boolean shutdown = false;
+
+  /**
+   * Indicates that event STREAM_START is passed over this link, so main loop
+   * can be started to pull data from source element.
+   */
+  protected boolean start;
+
+  /**
+   * Operate in pull mode.
+   */
+  protected boolean pullMode;
+
+  public SyncLink() {
+  }
+
+  public SyncLink(String id) {
+    this.id = id;
+  }
+
+  @Override
+  public void pushBack(ByteBuffer buf) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Buffer pushed back: " + buf + ".");
+
+    if (cacheBuffer != null) {
+      ByteBuffer tmp = cacheBuffer.join(buf);
+      cacheBuffer.unref();
+      cacheBuffer = tmp;
+    } else {
+      cacheBuffer = buf;
+      cacheBuffer.ref();
+    }
+
+    resetCursor();
+  }
+
+  private void resetCursor() {
+    // Reset cursor
+    cacheBuffer.cursor = 0;
+  }
+
+  @Override
+  public void pushBack(ByteBuffer buf, int lengthOfFullPacket) {
+    pushBack(buf);
+    expectedPacketSize = lengthOfFullPacket;
+  }
+
+  @Override
+  public String toString() {
+    return "SyncLink(" + ((id != null) ? id + ", " : "") + source + ":" + sink + ")";
+  }
+
+  /**
+   * Push data to sink. Call with null to push cached data.
+   */
+  @Override
+  public void sendData(ByteBuffer buf) {
+    if (!paused && pullMode)
+      throw new RuntimeException("[" + this + "] ERROR: link is not in push mode.");
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Incoming buffer: " + buf + ".");
+
+    if (buf == null && cacheBuffer == null)
+      return;
+
+    if (cacheBuffer != null && buf != null) {
+      // Join old data with fresh data
+      buf = cacheBuffer.join(buf);
+      cacheBuffer.unref();
+      cacheBuffer = buf;
+    }
+
+    // Store buffer in cache field to simplify following loop
+    if (buf != null)
+      cacheBuffer = buf;
+
+    // When data pushed back and length of data is less than length of full
+    // packet, then feed data to sink element immediately
+    while (cacheBuffer != null) {
+      if (paused) {
+        if (verbose)
+          System.out.println("[" + this + "] INFO: Transfer is paused. Data in cache buffer: " + cacheBuffer + ".");
+
+        // Wait until rest of packet will be read
+        return;
+      }
+
+      if (expectedPacketSize > 0 && cacheBuffer.length < expectedPacketSize) {
+        if (verbose)
+          System.out.println("[" + this + "] INFO: Transfer is suspended because available data is less than expected packet size. Expected packet size: "
+              + expectedPacketSize + ", data in cache buffer: " + cacheBuffer + ".");
+
+        // Wait until rest of packet will be read
+        return;
+      }
+
+      // Full packet or packet header is read, feed it to element
+      buf = cacheBuffer;
+      cacheBuffer = null;
+      expectedPacketSize = 0;
+      packetNumber++;
+
+      if (sink == null)
+        throw new NullPointerException("[" + this + "] ERROR: Cannot send data to sink: sink is null. Data: " + buf + ".");
+
+      sink.handleData(buf, this);
+      // cacheBuffer and expectedPacketSize can be changed at this time
+    }
+
+  }
+
+  @SuppressWarnings("incomplete-switch")
+  @Override
+  public void sendEvent(Event event, Direction direction) {
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Event " + event + " is received.");
+
+    // Shutdown main loop (if any) when STREAM_CLOSE event is received.
+    switch (event) {
+    case STREAM_START: {
+      if (!start)
+        start = true;
+      else
+        // Event already sent trough this link
+        return;
+      break;
+    }
+    case STREAM_CLOSE: {
+      if (!shutdown)
+        shutdown = true;
+      else
+        // Event already sent trough this link
+        return;
+      break;
+    }
+    case LINK_SWITCH_TO_PULL_MODE: {
+      setPullMode();
+      break;
+    }
+
+    }
+
+    switch (direction) {
+    case IN:
+      source.handleEvent(event, direction);
+      break;
+    case OUT:
+      sink.handleEvent(event, direction);
+      break;
+    }
+  }
+
+  @Override
+  public ByteBuffer pull(boolean block) {
+    if (!pullMode)
+      throw new RuntimeException("This link is not in pull mode.");
+
+    if (paused) {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Cannot pull, link is paused.");
+
+      // Make slight delay in such case, to avoid consuming 100% of CPU
+      if (block) {
+        try {
+          Thread.sleep(100);
+        } catch (InterruptedException e) {
+        }
+      }
+
+      return null;
+    }
+
+    // If data in cache can be sent immediately,
+    // then return it instead of asking for more data from source
+    if (cacheBuffer != null && (expectedPacketSize == 0 || (expectedPacketSize > 0 && cacheBuffer.length >= expectedPacketSize))) {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Data pulled from cache buffer: " + cacheBuffer + ".");
+
+      ByteBuffer tmp = cacheBuffer;
+      cacheBuffer = null;
+      return tmp;
+    }
+
+    // Pause this link, so incoming data will not be sent to sink
+    // immediately, then ask source element for more data
+    pause();
+    source.poll(block);
+    resume();
+
+    // Can return something only when data was stored in buffer
+    if (cacheBuffer != null && (expectedPacketSize == 0 || (expectedPacketSize > 0 && cacheBuffer.length >= expectedPacketSize))) {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Data pulled from source: " + cacheBuffer + ".");
+
+      ByteBuffer tmp = cacheBuffer;
+      cacheBuffer = null;
+      return tmp;
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public Element setSink(Element sink) {
+    if (sink != null && this.sink != null)
+      throw new RuntimeException("This link sink element is already set. Link: " + this + ", new sink: " + sink + ", existing sink: " + this.sink + ".");
+
+    if (sink == null && cacheBuffer != null)
+      throw new RuntimeException("Cannot drop link: cache is not empty. Link: " + this + ", cache: " + cacheBuffer);
+
+    this.sink = sink;
+
+    return sink;
+  }
+
+  @Override
+  public Element setSource(Element source) {
+    if (this.source != null && source != null)
+      throw new RuntimeException("This link source element is already set. Link: " + this + ", new source: " + source + ", existing source: " + this.source
+          + ".");
+
+    this.source = source;
+    return source;
+  }
+
+  @Override
+  public Element getSource() {
+    return source;
+  }
+
+  @Override
+  public Element getSink() {
+    return sink;
+  }
+
+  @Override
+  public void pause() {
+    if (paused)
+      throw new RuntimeException("Link is already paused.");
+
+    paused = true;
+
+  }
+
+  @Override
+  public void resume() {
+    paused = false;
+  }
+
+  /**
+   * Run pull loop to actively pull data from source and push it to sink. It
+   * must be only one pull loop per thread.
+   * 
+   * Pull loop will start after event STREAM_START. This link and source element
+   * incomming links will be switched to pull mode before pull loop will be
+   * started using event LINK_SWITCH_TO_PULL_MODE.
+   */
+  @Override
+  public void run() {
+    // Wait until even STREAM_START will arrive
+    while (!start) {
+      delay();
+    }
+
+    sendEvent(Event.LINK_SWITCH_TO_PULL_MODE, Direction.IN);
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Starting pull loop.");
+
+    // Pull source in loop
+    while (!shutdown) {
+      // Pull data from source element and send it to sink element
+      ByteBuffer data = pull(true);
+      if (data != null)
+        sink.handleData(data, this);
+
+      if (!shutdown && data == null) {
+        // Make slight delay to avoid consuming of 100% of CPU
+        delay();
+      }
+    }
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Pull loop finished.");
+
+  }
+
+  protected void delay() {
+    try {
+      Thread.sleep(delay);
+    } catch (InterruptedException e) {
+      e.printStackTrace(System.err);
+      throw new RuntimeException("Interrupted in main loop.", e);
+    }
+  }
+
+  @Override
+  public void setPullMode() {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Switching to PULL mode.");
+
+    this.pullMode = true;
+  }
+
+  @Override
+  public void drop() {
+    if (pullMode)
+      throw new RuntimeException("Cannot drop link in pull mode.");
+
+    if (cacheBuffer != null)
+      throw new RuntimeException("Cannot drop link when cache conatains data: " + cacheBuffer + ".");
+
+    source.dropLink(this);
+    sink.dropLink(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java
new file mode 100644
index 0000000..5537d24
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java
@@ -0,0 +1,369 @@
+// 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 vncclient;
+
+import java.awt.event.KeyEvent;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+import common.KeyOrder;
+
+public class AwtKeyboardEventToVncAdapter extends BaseElement {
+
+  protected boolean sh = false;
+  protected boolean caps = false;
+  protected boolean num = false;
+
+  public AwtKeyboardEventToVncAdapter(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(8);
+    outBuf.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT);
+
+    outBuf.writeByte((order.pressed) ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP);
+    outBuf.writeShort(0); // padding
+    outBuf.writeInt(map_en_us(order));
+
+    pushDataToAllOuts(outBuf);
+  }
+
+  /**
+   * Return key scan code (in lower byte) and extended flags (in second byte).
+   */
+  private int map_en_us(KeyOrder order) {
+
+    switch (order.event.getKeyCode()) {
+    // Functional keys
+    case KeyEvent.VK_ESCAPE:
+      return 0xff1b;
+    case KeyEvent.VK_F1:
+      return 0xffbe;
+    case KeyEvent.VK_F2:
+      return 0xffbf;
+    case KeyEvent.VK_F3:
+      return 0xffc0;
+    case KeyEvent.VK_F4:
+      return 0xffc1;
+    case KeyEvent.VK_F5:
+      return 0xffc2;
+    case KeyEvent.VK_F6:
+      return 0xffc3;
+    case KeyEvent.VK_F7:
+      return 0xffc4;
+    case KeyEvent.VK_F8:
+      return 0xffc5;
+    case KeyEvent.VK_F9:
+      return 0xffc6;
+    case KeyEvent.VK_F10:
+      return 0xffc7;
+    case KeyEvent.VK_F11:
+      return 0xffc8;
+    case KeyEvent.VK_F12:
+      return 0xffc9;
+
+      // Row #1
+    case KeyEvent.VK_BACK_QUOTE:
+      return (sh) ? '~' : '`';
+    case KeyEvent.VK_1:
+      return (sh) ? '!' : '1';
+    case KeyEvent.VK_2:
+      return (sh) ? '@' : '2';
+    case KeyEvent.VK_3:
+      return (sh) ? '#' : '3';
+    case KeyEvent.VK_4:
+      return (sh) ? '$' : '4';
+    case KeyEvent.VK_5:
+      return (sh) ? '%' : '5';
+    case KeyEvent.VK_6:
+      return (sh) ? '^' : '6';
+    case KeyEvent.VK_7:
+      return (sh) ? '&' : '7';
+    case KeyEvent.VK_8:
+      return (sh) ? '*' : '8';
+    case KeyEvent.VK_9:
+      return (sh) ? '(' : '9';
+    case KeyEvent.VK_0:
+      return (sh) ? ')' : '0';
+    case KeyEvent.VK_MINUS:
+      return (sh) ? '_' : '-';
+    case KeyEvent.VK_EQUALS:
+      return (sh) ? '+' : '=';
+    case KeyEvent.VK_BACK_SPACE:
+      return 0xff08;
+
+      // Row #2
+    case KeyEvent.VK_TAB:
+      return 0xff09;
+    case KeyEvent.VK_Q:
+      return (sh ^ caps) ? 'Q' : 'q';
+    case KeyEvent.VK_W:
+      return (sh ^ caps) ? 'W' : 'w';
+    case KeyEvent.VK_E:
+      return (sh ^ caps) ? 'E' : 'e';
+    case KeyEvent.VK_R:
+      return (sh ^ caps) ? 'R' : 'r';
+    case KeyEvent.VK_T:
+      return (sh ^ caps) ? 'T' : 't';
+    case KeyEvent.VK_Y:
+      return (sh ^ caps) ? 'Y' : 'y';
+    case KeyEvent.VK_U:
+      return (sh ^ caps) ? 'U' : 'u';
+    case KeyEvent.VK_I:
+      return (sh ^ caps) ? 'I' : 'i';
+    case KeyEvent.VK_O:
+      return (sh ^ caps) ? 'O' : 'o';
+    case KeyEvent.VK_P:
+      return (sh ^ caps) ? 'P' : 'p';
+    case KeyEvent.VK_OPEN_BRACKET:
+      return (sh) ? '{' : '[';
+    case KeyEvent.VK_CLOSE_BRACKET:
+      return (sh) ? '{' : ']';
+    case KeyEvent.VK_ENTER:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_STANDARD:
+        return 0xff0d;
+      case KeyEvent.KEY_LOCATION_NUMPAD:
+        return 0xff8d;
+      }
+
+      // Row #3
+    case KeyEvent.VK_CAPS_LOCK:
+      if (order.pressed)
+        caps = !caps;
+      return 0xFFE5;
+    case KeyEvent.VK_A:
+      return (sh ^ caps) ? 'A' : 'a';
+    case KeyEvent.VK_S:
+      return (sh ^ caps) ? 'S' : 's';
+    case KeyEvent.VK_D:
+      return (sh ^ caps) ? 'D' : 'd';
+    case KeyEvent.VK_F:
+      return (sh ^ caps) ? 'F' : 'f';
+    case KeyEvent.VK_G:
+      return (sh ^ caps) ? 'G' : 'g';
+    case KeyEvent.VK_H:
+      return (sh ^ caps) ? 'H' : 'h';
+    case KeyEvent.VK_J:
+      return (sh ^ caps) ? 'J' : 'j';
+    case KeyEvent.VK_K:
+      return (sh ^ caps) ? 'K' : 'k';
+    case KeyEvent.VK_L:
+      return (sh ^ caps) ? 'L' : 'l';
+    case KeyEvent.VK_SEMICOLON:
+      return (sh) ? ':' : ';';
+    case KeyEvent.VK_QUOTE:
+      return (sh) ? '"' : '\'';
+
+      // Row #4
+    case KeyEvent.VK_SHIFT:
+      sh = !sh;
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xffe1;
+      case KeyEvent.KEY_LOCATION_RIGHT:
+        return 0xffe2;
+      }
+    case KeyEvent.VK_BACK_SLASH:
+      return (sh) ? '|' : '\\';
+    case KeyEvent.VK_Z:
+      return (sh ^ caps) ? 'Z' : 'z';
+    case KeyEvent.VK_X:
+      return (sh ^ caps) ? 'X' : 'x';
+    case KeyEvent.VK_C:
+      return (sh ^ caps) ? 'C' : 'c';
+    case KeyEvent.VK_V:
+      return (sh ^ caps) ? 'V' : 'v';
+    case KeyEvent.VK_B:
+      return (sh ^ caps) ? 'B' : 'b';
+    case KeyEvent.VK_N:
+      return (sh ^ caps) ? 'N' : 'n';
+    case KeyEvent.VK_M:
+      return (sh ^ caps) ? 'M' : 'M';
+    case KeyEvent.VK_COMMA:
+      return (sh) ? '<' : ',';
+    case KeyEvent.VK_PERIOD:
+      return (sh) ? '>' : '.';
+    case KeyEvent.VK_SLASH:
+      return (sh) ? '?' : '/';
+
+      //
+      // Bottom row
+    case KeyEvent.VK_CONTROL:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xFFE3;
+      case KeyEvent.KEY_LOCATION_RIGHT:
+        return 0xFFE4;
+      }
+    case KeyEvent.VK_WINDOWS:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xFFED; // HyperL
+      case KeyEvent.KEY_LOCATION_RIGHT:
+        return 0xFFEE; // HyperR
+      }
+    case KeyEvent.VK_META:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xFFE7; // MetaL
+      case KeyEvent.KEY_LOCATION_RIGHT:
+        return 0xFFE8; // MetaR
+      }
+
+    case KeyEvent.VK_ALT:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xFFE9;
+      case KeyEvent.KEY_LOCATION_RIGHT:
+        return 0xFFEA;
+      }
+    case KeyEvent.VK_ALT_GRAPH:
+      return 0xfe03;
+
+    case KeyEvent.VK_SPACE:
+      return ' ';
+
+    case KeyEvent.VK_CONTEXT_MENU:
+      return 0xff67;
+
+      //
+      // Special keys
+    case KeyEvent.VK_PRINTSCREEN:
+      return (sh) ? 0xFF15/* SysRq */: 0xFF61 /* Print */;
+    case KeyEvent.VK_SCROLL_LOCK:
+      return 0xFF14;
+    case KeyEvent.VK_PAUSE:
+      return (sh) ? 0xFF6B/* Break */: 0xFF13/* Pause */;
+
+      // Text navigation keys
+    case KeyEvent.VK_INSERT:
+      return 0xff63;
+    case KeyEvent.VK_DELETE:
+      return 0xffff;
+    case KeyEvent.VK_HOME:
+      return 0xff50;
+    case KeyEvent.VK_END:
+      return 0xff57;
+    case KeyEvent.VK_PAGE_UP:
+      return 0xff55;
+    case KeyEvent.VK_PAGE_DOWN:
+      return 0xff56;
+
+      // Cursor keys
+    case KeyEvent.VK_LEFT:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xff51;
+      case KeyEvent.KEY_LOCATION_NUMPAD:
+        return 0xFF96;
+      }
+    case KeyEvent.VK_UP:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xff52;
+      case KeyEvent.KEY_LOCATION_NUMPAD:
+        return 0xFF97;
+      }
+    case KeyEvent.VK_RIGHT:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xff53;
+      case KeyEvent.KEY_LOCATION_NUMPAD:
+        return 0xFF98;
+      }
+    case KeyEvent.VK_DOWN:
+      switch (order.event.getKeyLocation()) {
+      default:
+      case KeyEvent.KEY_LOCATION_LEFT:
+        return 0xff54;
+      case KeyEvent.KEY_LOCATION_NUMPAD:
+        return 0xFF99;
+      }
+
+      // Keypad
+    case KeyEvent.VK_NUM_LOCK:
+      if (order.pressed)
+        num = !num;
+      return 0xFF6F;
+    case KeyEvent.VK_DIVIDE:
+      return 0xFFAF;
+    case KeyEvent.VK_MULTIPLY:
+      return 0xFFAA;
+    case KeyEvent.VK_SUBTRACT:
+      return 0xFFAD;
+    case KeyEvent.VK_ADD:
+      return 0xFFAB;
+
+    case KeyEvent.VK_KP_LEFT:
+      return 0xFF96;
+    case KeyEvent.VK_KP_UP:
+      return 0xFF97;
+    case KeyEvent.VK_KP_RIGHT:
+      return 0xFF98;
+    case KeyEvent.VK_KP_DOWN:
+      return 0xFF99;
+
+    case KeyEvent.VK_NUMPAD0:
+      return 0xFFB0;
+    case KeyEvent.VK_NUMPAD1:
+      return 0xFFB1;
+    case KeyEvent.VK_NUMPAD2:
+      return 0xFFB2;
+    case KeyEvent.VK_NUMPAD3:
+      return 0xFFB3;
+    case KeyEvent.VK_NUMPAD4:
+      return 0xFFB4;
+    case KeyEvent.VK_NUMPAD5:
+      return 0xFFB5;
+    case KeyEvent.VK_NUMPAD6:
+      return 0xFFB6;
+    case KeyEvent.VK_NUMPAD7:
+      return 0xFFB7;
+    case KeyEvent.VK_NUMPAD8:
+      return 0xFFB8;
+    case KeyEvent.VK_NUMPAD9:
+      return 0xFFB9;
+    case KeyEvent.VK_DECIMAL:
+      return 0xFFAE;
+
+    default:
+      System.err.println("Key is not mapped: " + order + ".");
+      return ' '; // Space
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java
new file mode 100644
index 0000000..dd93394
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java
@@ -0,0 +1,71 @@
+// 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 vncclient;
+
+import java.awt.event.MouseEvent;
+
+import common.MouseOrder;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class AwtMouseEventToVncAdapter extends BaseElement {
+
+  public AwtMouseEventToVncAdapter(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(6);
+
+    outBuf.writeByte(RfbConstants.CLIENT_POINTER_EVENT);
+
+    int buttonMask = mapAwtModifiersToVncButtonMask(order.event.getModifiersEx());
+    outBuf.writeByte(buttonMask);
+    outBuf.writeShort(order.event.getX());
+    outBuf.writeShort(order.event.getY());
+    
+    pushDataToAllOuts(outBuf);
+  }
+
+  /**
+   * Current state of buttons 1 to 8 are represented by bits 0 to 7 of
+   * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a
+   * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and
+   * right buttons on the mouse. On a wheel mouse, each step of the wheel
+   * upwards is represented by a press and release of button 4, and each step
+   * downwards is represented by a press and release of button 5.
+   * 
+   * @param modifiers
+   *          extended modifiers from AWT mouse event
+   * @return VNC mouse button mask
+   */
+  public static int mapAwtModifiersToVncButtonMask(int modifiers) {
+    int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0)
+        | (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0);
+    return mask;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java
new file mode 100644
index 0000000..9ee3566
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java
@@ -0,0 +1,63 @@
+// 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 vncclient;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class EncodingsMessage extends BaseElement {
+
+  protected final int[] encodings;
+
+  public EncodingsMessage(String id, int[] encodings) {
+    super(id);
+    this.encodings = encodings;
+    declarePads();
+  }
+
+  protected void declarePads() {
+    inputPads.put(STDIN, null);
+    outputPads.put(STDOUT, null);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+    buf.unref();
+
+    ByteBuffer outBuf = new ByteBuffer(4 + encodings.length * 4);
+
+    outBuf.writeByte(RfbConstants.CLIENT_SET_ENCODINGS);
+
+    outBuf.writeByte(0);// padding
+
+    outBuf.writeShort(encodings.length);
+
+    for (int i = 0; i < encodings.length; i++) {
+      outBuf.writeInt(encodings[i]);
+    }
+
+    pushDataToAllOuts(outBuf);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java
new file mode 100644
index 0000000..c8fab21
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java
@@ -0,0 +1,127 @@
+// 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 vncclient;
+
+import common.ScreenDescription;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class FrameBufferUpdateRequest extends BaseElement {
+  // TODO: use object with fields instead of raw values in map
+  public static final String INCREMENTAL_UPDATE = "incremental";
+  public static final String TARGET_X = "x";
+  public static final String TARGET_Y = "y";
+  public static final String WIDTH = "width";
+  public static final String HEIGHT = "height";
+
+  protected ScreenDescription screen;
+
+  public FrameBufferUpdateRequest(String id, ScreenDescription screen) {
+    super(id);
+    this.screen = screen;
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    Boolean incremental = (Boolean) buf.getMetadata(INCREMENTAL_UPDATE);
+    Integer x = (Integer) buf.getMetadata(TARGET_X);
+    Integer y = (Integer) buf.getMetadata(TARGET_Y);
+    Integer width = (Integer) buf.getMetadata(WIDTH);
+    Integer height = (Integer) buf.getMetadata(HEIGHT);
+    buf.unref();
+
+    // Set default values when parameters are not set
+    if (incremental == null)
+      incremental = false;
+
+    if (x == null)
+      x = 0;
+    if (y == null)
+      y = 0;
+
+    if (width == null)
+      width = screen.getFramebufferWidth();
+    if (height == null)
+      height = screen.getFramebufferHeight();
+
+    ByteBuffer outBuf = new ByteBuffer(10);
+
+    outBuf.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST);
+    outBuf.writeByte((incremental) ? RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST : RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST);
+    outBuf.writeShort(x);
+    outBuf.writeShort(y);
+    outBuf.writeShort(width);
+    outBuf.writeShort(height);
+
+    if (verbose) {
+      outBuf.putMetadata("sender", this);
+      outBuf.putMetadata("dimensions", width + "x" + height + "@" + x + "x" + y);
+    }
+
+    pushDataToAllOuts(outBuf);
+  }
+
+  public static void main(String args[]) {
+    System.setProperty("streamer.Element.debug", "true");
+
+    ScreenDescription screen = new ScreenDescription();
+    screen.setFramebufferSize(120, 80);
+    Element adapter = new FrameBufferUpdateRequest("renderer", screen);
+
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+        // Request
+        RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST,
+        // Full update (redraw area)
+        RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST,
+        // X
+        0, 1,
+        // Y
+        0, 2,
+        // Width
+        0, 3,
+        // Height
+        0, 4 }));
+
+    ByteBuffer buf = new ByteBuffer(new byte[0]);
+    buf.putMetadata(TARGET_X, 1);
+    buf.putMetadata(TARGET_Y, 2);
+    buf.putMetadata(WIDTH, 3);
+    buf.putMetadata(HEIGHT, 4);
+
+    Element source = new MockSource("source", new ByteBuffer[] { buf });
+
+    Pipeline pipeline = new PipelineImpl("test");
+
+    pipeline.addAndLink(source, adapter, sink);
+    pipeline.runMainLoop("source", STDOUT, false, false);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java
new file mode 100644
index 0000000..8c691e7
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java
@@ -0,0 +1,90 @@
+// 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 vncclient;
+
+import common.ScreenDescription;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class RGB888LE32PixelFormatRequest extends BaseElement {
+  protected int bitsPerPixel = 32;
+  protected int depth = 24;
+  protected int bigEndianFlag = RfbConstants.LITTLE_ENDIAN;
+  protected int trueColourFlag = RfbConstants.TRUE_COLOR;
+  protected int redMax = 255;
+  protected int greenMax = 255;
+  protected int blueMax = 255;
+  protected int redShift = 0;
+  protected int greenShift = 8;
+  protected int blueShift = 16;
+
+  protected ScreenDescription screen;
+
+  public RGB888LE32PixelFormatRequest(String id, ScreenDescription screen) {
+    super(id);
+    this.screen = screen;
+  }
+
+  protected void declarePads() {
+    inputPads.put(STDIN, null);
+    outputPads.put(STDOUT, null);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+    buf.unref();
+
+    ByteBuffer outBuf = new ByteBuffer(20);
+
+    outBuf.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT);
+
+    // Padding
+    outBuf.writeByte(0);
+    outBuf.writeByte(0);
+    outBuf.writeByte(0);
+
+    // Send pixel format
+    outBuf.writeByte(bitsPerPixel);
+    outBuf.writeByte(depth);
+    outBuf.writeByte(bigEndianFlag);
+    outBuf.writeByte(trueColourFlag);
+    outBuf.writeShort(redMax);
+    outBuf.writeShort(greenMax);
+    outBuf.writeShort(blueMax);
+    outBuf.writeByte(redShift);
+    outBuf.writeByte(greenShift);
+    outBuf.writeByte(blueShift);
+
+    // Padding
+    outBuf.writeByte(0);
+    outBuf.writeByte(0);
+    outBuf.writeByte(0);
+
+    screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColourFlag == RfbConstants.TRUE_COLOR, redMax, greenMax,
+        blueMax, redShift, greenShift, blueShift);
+
+    pushDataToAllOuts(outBuf);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.java
new file mode 100644
index 0000000..c2d63bb
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.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 vncclient;
+
+import java.nio.charset.Charset;
+
+public interface RfbConstants {
+
+  public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003.";
+  public static final String VNC_PROTOCOL_VERSION_MINOR = "003";
+  public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR;
+
+  /**
+   * Server message types.
+   */
+  final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3;
+
+  /**
+   * Client message types.
+   */
+  public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3,
+      CLIENT_KEYBOARD_EVENT = 4, CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6;
+
+  /**
+   * Server authorization type
+   */
+  public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2;
+
+  /**
+   * Server authorization reply.
+   */
+  public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2;
+
+  /**
+   * Encodings.
+   */
+  public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16;
+
+  /**
+   * Pseudo-encodings.
+   */
+  public final static int ENCODING_CURSOR = -239 /*0xFFFFFF11*/, ENCODING_DESKTOP_SIZE = -223 /*0xFFFFFF21*/;
+
+  /**
+   * Encodings, which we support.
+   */
+  public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE };
+
+  /**
+   * Frame buffer update request type: update of whole screen or partial update.
+   */
+  public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1;
+
+  public static final int KEY_UP = 0, KEY_DOWN = 1;
+
+  public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1;
+
+  public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1;
+
+  public static final int PALETTE = 0, TRUE_COLOR = 1;
+
+  /**
+   * Default 8 bit charset to use when communicating with server.
+   */
+  public static final Charset US_ASCII_CHARSET = Charset.availableCharsets().get("US-ASCII");
+  
+  /**
+   * Default 16 bit charset to use when communicating with server.
+   */
+  public static final Charset UCS2_CHARSET = Charset.availableCharsets().get("UTF-16LE");
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java
new file mode 100644
index 0000000..2b77e0a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java
@@ -0,0 +1,107 @@
+// 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 vncclient;
+
+import streamer.PipelineImpl;
+import streamer.Queue;
+
+import common.AwtBellAdapter;
+import common.AwtCanvasAdapter;
+import common.AwtClipboardAdapter;
+import common.AwtKeyEventSource;
+import common.AwtMouseEventSource;
+import common.BufferedImageCanvas;
+import common.ScreenDescription;
+
+public class VncClient extends PipelineImpl {
+
+  public VncClient(String id, String password, ScreenDescription screen, BufferedImageCanvas canvas) {
+    super(id);
+    assembleVNCPipeline(password, screen, canvas);
+  }
+
+  private void assembleVNCPipeline(String password, ScreenDescription screen, BufferedImageCanvas canvas) {
+
+    AwtMouseEventSource mouseEventSource = new AwtMouseEventSource("mouse");
+    AwtKeyEventSource keyEventSource = new AwtKeyEventSource("keyboard");
+
+    // Subscribe packet sender to various events
+    canvas.addMouseListener(mouseEventSource);
+    canvas.addMouseMotionListener(mouseEventSource);
+    canvas.addKeyListener(keyEventSource);
+
+    add(
+    // Handshake
+
+    // RFB protocol version exchanger
+    new Vnc_3_3_Hello("hello"),
+    // Authenticator
+        new Vnc_3_3_Authentication("auth", password),
+        // Initializer
+        new VncInitializer("init", true, screen),
+
+        new EncodingsMessage("encodings", RfbConstants.SUPPORTED_ENCODINGS_ARRAY),
+
+        new RGB888LE32PixelFormatRequest("pixel_format", screen),
+
+        // Main
+
+        // Packet receiver
+        new VncMessageHandler("message_handler", screen),
+
+        new AwtBellAdapter("bell"),
+
+        new AwtClipboardAdapter("clipboard"),
+
+        new AwtCanvasAdapter("pixels", canvas, screen),
+
+        new Queue("queue"),
+
+        new FrameBufferUpdateRequest("fbur", screen),
+
+        new AwtKeyboardEventToVncAdapter("keyboard_adapter"),
+
+        new AwtMouseEventToVncAdapter("mouse_adapter"),
+
+        mouseEventSource, keyEventSource
+
+    );
+
+    // Link handshake elements
+    link("IN", "hello", "auth", "init", "message_handler");
+    link("hello >otout", "hello< OUT");
+    link("auth >otout", "auth< OUT");
+    link("init >otout", "init< OUT");
+    link("init >encodings", "encodings");
+    link("init >pixel_format", "pixel_format");
+    link("encodings", "encodings< OUT");
+    link("pixel_format", "pixel_format< OUT");
+
+    // Link main elements
+    link("message_handler >bell", "bell");
+    link("message_handler >clipboard", "clipboard");
+    link("message_handler >pixels", "pixels");
+    link("message_handler >fbur", "fbur");
+
+    link("fbur", "fbur< queue");
+    link("keyboard", "keyboard_adapter", "keyboard< queue");
+    link("mouse", "mouse_adapter", "mouse< queue");
+    link("queue", "OUT");
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java
new file mode 100644
index 0000000..0882d13
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java
@@ -0,0 +1,244 @@
+// 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 vncclient;
+
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.OneTimeSwitch;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+import common.ScreenDescription;
+
+public class VncInitializer extends OneTimeSwitch {
+
+  // Pad names
+  public static final String CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD = "encodings";
+  public static final String CLIENT_PIXEL_FORMAT_ADAPTER_PAD = "pixel_format";
+
+  protected byte sharedFlag = RfbConstants.EXCLUSIVE_ACCESS;
+
+  /**
+   * Properties of remote screen .
+   */
+  protected ScreenDescription screen;
+
+  public VncInitializer(String id, boolean shared, ScreenDescription screen) {
+    super(id);
+
+    setSharedFlag(shared);
+    this.screen = screen;
+
+    declarePads();
+  }
+
+  @Override
+  protected void declarePads() {
+    super.declarePads();
+    outputPads.put(CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, null);
+    outputPads.put(CLIENT_PIXEL_FORMAT_ADAPTER_PAD, null);
+  }
+
+  public ScreenDescription getScreen() {
+    return screen;
+  }
+
+  public void setScreen(ScreenDescription screen) {
+    this.screen = screen;
+  }
+
+  public void setSharedFlag(boolean shared) {
+    if (shared)
+      sharedFlag = RfbConstants.SHARED_ACCESS;
+    else
+      sharedFlag = RfbConstants.EXCLUSIVE_ACCESS;
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    // Server initialization message is at least 24 bytes long + length of
+    // desktop name
+    if (!cap(buf, 24, UNLIMITED, link, false))
+      return;
+
+    // Read server initialization message
+    // Read frame buffer size
+    int framebufferWidth = buf.readUnsignedShort();
+    int framebufferHeight = buf.readUnsignedShort();
+
+    // Read pixel format
+    int bitsPerPixel = buf.readUnsignedByte();
+    int depth = buf.readUnsignedByte();
+
+    int bigEndianFlag = buf.readUnsignedByte();
+    int trueColorFlag = buf.readUnsignedByte();
+
+    int redMax = buf.readUnsignedShort();
+    int greenMax = buf.readUnsignedShort();
+    int blueMax = buf.readUnsignedShort();
+
+    int redShift = buf.readUnsignedByte();
+    int greenShift = buf.readUnsignedByte();
+    int blueShift = buf.readUnsignedByte();
+
+    // Skip padding
+    buf.skipBytes(3);
+
+    // Read desktop name
+    int length = buf.readSignedInt();
+
+    // Consume exactly $length bytes, push back any extra bytes
+    if (!cap(buf, length, length, link, true))
+      return;
+
+    String desktopName = buf.readString(length, RfbConstants.US_ASCII_CHARSET);
+    buf.unref();
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Desktop name: \"" + desktopName + "\", bpp: " + bitsPerPixel + ", depth: " + depth + ", screen size: "
+          + framebufferWidth + "x" + framebufferHeight + ".");
+
+    // Set screen properties
+    screen.setFramebufferSize(framebufferWidth, framebufferHeight);
+    screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag!=RfbConstants.LITTLE_ENDIAN, trueColorFlag==RfbConstants.TRUE_COLOR, redMax, greenMax, blueMax, redShift, greenShift, blueShift);
+    screen.setDesktopName(desktopName);
+
+    // If sever screen has different parameters than ours, then change it
+    if (!screen.isRGB888_32_LE()) {
+      // Send client pixel format
+      sendClientPixelFormat();
+    }
+
+    // Send encodings supported by client
+    sendSupportedEncodings();
+
+    switchOff();
+
+  }
+
+  @Override
+  protected void onStart() {
+    ByteBuffer buf = new ByteBuffer(new byte[] { sharedFlag });
+    pushDataToOTOut(buf);
+  }
+
+  private void sendClientPixelFormat() {
+    pushDataToPad(CLIENT_PIXEL_FORMAT_ADAPTER_PAD, new ByteBuffer(0));
+  }
+
+  private void sendSupportedEncodings() {
+    pushDataToPad(CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, new ByteBuffer(0));
+  }
+
+  public String toString() {
+    return "VncInit(" + id + ")";
+  }
+
+  /**
+   * 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");
+
+    final String desktopName = "test";
+
+    Element source = new MockSource("source") {
+      {
+        bufs = ByteBuffer.convertByteArraysToByteBuffers(
+        // Send screen description
+            new byte[] {
+                // Framebuffer width (short)
+                0, (byte) 200,
+                // Framebuffer height (short)
+                0, 100,
+                // Bits per pixel
+                32,
+                // Depth,
+                24,
+                // Endianness flag
+                RfbConstants.LITTLE_ENDIAN,
+                // Truecolor flag
+                RfbConstants.TRUE_COLOR,
+                // Red max (short)
+                0, (byte) 255,
+                // Green max (short)
+                0, (byte) 255,
+                // Blue max (short)
+                0, (byte) 255,
+                // Red shift
+                16,
+                // Green shift
+                8,
+                // Blue shift
+                0,
+                // Padding
+                0, 0, 0,
+                // Desktop name length (int)
+                0, 0, 0, 4,
+                // Desktop name ("test", 4 bytes)
+                't', 'e', 's', 't',
+
+                // Tail
+                1, 2, 3
+
+            },
+            // Tail packet
+            new byte[] { 4, 5, 6 });
+      }
+    };
+
+    ScreenDescription screen = new ScreenDescription();
+    final VncInitializer init = new VncInitializer("init", true, screen);
+    Element initSink = new MockSink("initSink") {
+      {
+        // Expect shared flag
+        bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] { RfbConstants.SHARED_ACCESS });
+      }
+    };
+    Element mainSink = new MockSink("mainSink") {
+      {
+        // Expect two tail packets
+        bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }, new byte[] { 4, 5, 6 });
+      }
+    };
+    ByteBuffer[] emptyBuf = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {});
+    Element encodingsSink = new MockSink("encodings", emptyBuf);
+    Element pixelFormatSink = new MockSink("pixel_format", emptyBuf);
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.addAndLink(source, init, mainSink);
+    pipeline.add(encodingsSink, pixelFormatSink, initSink);
+    pipeline.link("init >otout", "initSink");
+    pipeline.link("init >" + CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, "encodings");
+    pipeline.link("init >" + CLIENT_PIXEL_FORMAT_ADAPTER_PAD, "pixel_format");
+
+    pipeline.runMainLoop("source", STDOUT, false, false);
+
+    if (!screen.isRGB888_32_LE())
+      System.err.println("Screen description was read incorrectly: " + screen + ".");
+    if (!desktopName.equals(screen.getDesktopName()))
+      System.err.println("Screen desktop name was read incorrectly: \"" + screen.getDesktopName() + "\".");
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java
new file mode 100644
index 0000000..758000d
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java
@@ -0,0 +1,419 @@
+// 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 vncclient;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+import common.BitmapOrder;
+import common.BitmapRectangle;
+import common.CopyRectOrder;
+import common.ScreenDescription;
+
+public class VncMessageHandler extends BaseElement {
+  protected ScreenDescription screen = null;
+
+  // Pad names
+  public static final String SERVER_BELL_ADAPTER_PAD = "bell";
+  public static final String SERVER_CLIPBOARD_ADAPTER_PAD = "clipboard";
+  public static final String PIXEL_ADAPTER_PAD = "pixels";
+  public static final String FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD = "fbur";
+
+  // Keys for metadata
+  public static final String CLIPBOARD_CONTENT = "content";
+  public static final String TARGET_X = "x";
+  public static final String TARGET_Y = "y";
+  public static final String WIDTH = "width";
+  public static final String HEIGHT = "height";
+  public static final String SOURCE_X = "srcX";
+  public static final String SOURCE_Y = "srcY";
+  public static final String PIXEL_FORMAT = "pixel_format";
+
+  private static final String NUM_OF_PROCESSED_RECTANGLES = "rects";
+  private static final String SAVED_CURSOR_POSITION = "cursor";
+
+  // Pixel format: RGB888 LE 32
+  public static final String RGB888LE32 = "RGB888LE32";
+
+  public VncMessageHandler(String id, ScreenDescription screen) {
+    super(id);
+    this.screen = screen;
+    declarePads();
+  }
+
+  private void declarePads() {
+    outputPads.put(SERVER_BELL_ADAPTER_PAD, null);
+    outputPads.put(SERVER_BELL_ADAPTER_PAD, null);
+    outputPads.put(SERVER_CLIPBOARD_ADAPTER_PAD, null);
+    outputPads.put(PIXEL_ADAPTER_PAD, null);
+    outputPads.put(FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, null);
+
+    inputPads.put("stdin", null);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    try {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+      if (!cap(buf, 1, UNLIMITED, link, false))
+        return;
+
+      // Read server message type
+      int messageType = buf.readUnsignedByte();
+
+      // Invoke packet handler by packet type.
+      switch (messageType) {
+
+      case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: {
+        // Handle frame buffer update
+        if (!handleFBU(buf, link))
+          return;
+
+        // Frame buffer update is received and fully processed, send request for
+        // another frame buffer update to server.
+        sendFBUR();
+
+        break;
+      }
+
+      case RfbConstants.SERVER_BELL: {
+        if (!handleBell(buf, link))
+          return;
+        break;
+      }
+
+      case RfbConstants.SERVER_CUT_TEXT: {
+        if (!handleClipboard(buf, link))
+          return;
+        break;
+      }
+
+      default:
+        // TODO: allow to extend functionality
+        throw new RuntimeException("Unknown server packet type: " + messageType + ".");
+      }
+
+      // Cut tail, if any
+      cap(buf, 0, 0, link, true);
+    } finally {
+
+      // Return processed buffer back to pool
+      buf.unref();
+    }
+  }
+
+  private boolean handleClipboard(ByteBuffer buf, Link link) {
+    if (!cap(buf, 3 + 4, UNLIMITED, link, true))
+      return false;
+
+    // Skip padding
+    buf.skipBytes(3);
+
+    // Read text length
+    int length = buf.readSignedInt();
+
+    // We need full string to parse it
+    if (!cap(buf, length, UNLIMITED, link, true))
+      return false;
+
+    String content = buf.readString(length, RfbConstants.US_ASCII_CHARSET);
+
+    // Send content in metadata
+    ByteBuffer outBuf = new ByteBuffer(0);
+    outBuf.putMetadata(CLIPBOARD_CONTENT, content);
+
+    pushDataToPad(SERVER_CLIPBOARD_ADAPTER_PAD, outBuf);
+
+    return true;
+  }
+
+  private boolean handleBell(ByteBuffer buf, Link link) {
+    // Send empty packet to bell adapter to produce bell
+    pushDataToPad(SERVER_BELL_ADAPTER_PAD, new ByteBuffer(0));
+
+    return true;
+  }
+
+  // FIXME: this method is too complex
+  private boolean handleFBU(ByteBuffer buf, Link link) {
+
+    // We need at least 3 bytes here, 1 - padding, 2 - number of rectangles
+    if (!cap(buf, 3, UNLIMITED, link, true))
+      return false;
+
+    buf.skipBytes(1);// Skip padding
+
+    // Read number of rectangles
+    int numberOfRectangles = buf.readUnsignedShort();
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Frame buffer update. Number of rectangles: " + numberOfRectangles + ".");
+
+    // Each rectangle must have header at least, header length is 12 bytes.
+    if (!cap(buf, 12 * numberOfRectangles, UNLIMITED, link, true))
+      return false;
+
+    // For all rectangles
+
+    // Restore saved point, to avoid flickering and performance problems when
+    // frame buffer update is split between few incoming packets.
+    int numberOfProcessedRectangles = (buf.getMetadata(NUM_OF_PROCESSED_RECTANGLES) != null) ? (Integer) buf.getMetadata(NUM_OF_PROCESSED_RECTANGLES) : 0;
+    if (buf.getMetadata(SAVED_CURSOR_POSITION) != null)
+      buf.cursor = (Integer) buf.getMetadata(SAVED_CURSOR_POSITION);
+
+    if (verbose && numberOfProcessedRectangles > 0)
+      System.out.println("[" + this + "] INFO: Restarting from saved point. Number of already processed rectangles: " + numberOfRectangles + ", cursor: "
+          + buf.cursor + ".");
+
+    // For all new rectangles
+    for (int i = numberOfProcessedRectangles; i < numberOfRectangles; i++) {
+
+      // We need coordinates of rectangle (2x4 bytes) and encoding type (4
+      // bytes)
+      if (!cap(buf, 12, UNLIMITED, link, true))
+        return false;
+
+      // Read coordinates of rectangle
+      int x = buf.readUnsignedShort();
+      int y = buf.readUnsignedShort();
+      int width = buf.readUnsignedShort();
+      int height = buf.readUnsignedShort();
+
+      // Read rectangle encoding
+      int encodingType = buf.readSignedInt();
+
+      // Process rectangle
+      switch (encodingType) {
+
+      case RfbConstants.ENCODING_RAW: {
+        if (!handleRawRectangle(buf, link, x, y, width, height))
+          return false;
+        break;
+      }
+
+      case RfbConstants.ENCODING_COPY_RECT: {
+        if (!handleCopyRect(buf, link, x, y, width, height))
+          return false;
+        break;
+      }
+
+      case RfbConstants.ENCODING_DESKTOP_SIZE: {
+        if (!handleScreenSizeChangeRect(buf, link, x, y, width, height))
+          return false;
+        break;
+      }
+
+      default:
+        // TODO: allow to extend functionality
+        throw new RuntimeException("Unsupported ecnoding: " + encodingType + ".");
+      }
+
+      // Update information about processed rectangles to avoid handling of same
+      // rectangle multiple times.
+      // TODO: push back partial rectangle only instead
+      buf.putMetadata(NUM_OF_PROCESSED_RECTANGLES, ++numberOfProcessedRectangles);
+      buf.putMetadata(SAVED_CURSOR_POSITION, buf.cursor);
+    }
+
+    return true;
+  }
+
+  private boolean handleScreenSizeChangeRect(ByteBuffer buf, Link link, int x, int y, int width, int height) {
+    // Remote screen size is changed
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Screen size rect. Width: " + width + ", height: " + height + ".");
+
+    screen.setFramebufferSize(width, height);
+
+    return true;
+  }
+
+  private boolean handleCopyRect(ByteBuffer buf, Link link, int x, int y, int width, int height) {
+    // Copy rectangle from one part of screen to another.
+    // Areas may overlap. Antialiasing may cause visible artifacts.
+
+    // We need 4 bytes with coordinates of source rectangle
+    if (!cap(buf, 4, UNLIMITED, link, true))
+      return false;
+
+    CopyRectOrder order = new CopyRectOrder();
+
+    order.srcX = buf.readUnsignedShort();
+    order.srcY = buf.readUnsignedShort();
+    order.x = x;
+    order.y = y;
+    order.width = width;
+    order.height = height;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Copy rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", srcX: " + order.srcX
+          + ", srcY: " + order.srcY + ".");
+
+    pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order));
+
+    return true;
+  }
+
+  private boolean handleRawRectangle(ByteBuffer buf, Link link, int x, int y, int width, int height) {
+    // Raw rectangle is just array of pixels to draw on screen.
+    int rectDataLength = width * height * screen.getBytesPerPixel();
+
+    // We need at least rectDataLength bytes. Extra bytes may contain other
+    // rectangles.
+    if (!cap(buf, rectDataLength, UNLIMITED, link, true))
+      return false;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Raw rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", data length: "
+          + rectDataLength + ".");
+
+    BitmapRectangle rectangle = new BitmapRectangle();
+    rectangle.x = x;
+    rectangle.y = y;
+    rectangle.width = width;
+    rectangle.height = height;
+    rectangle.bufferWidth = width;
+    rectangle.bufferHeight = height;
+    rectangle.bitmapDataStream = buf.readBytes(rectDataLength);
+    rectangle.colorDepth=screen.getColorDeph();
+
+    BitmapOrder order = new BitmapOrder();
+    order.rectangles = new BitmapRectangle[] { rectangle };
+    
+    pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order));
+    return true;
+  }
+
+  public void onStart() {
+    // Send Frame Buffer Update request
+    sendFBUR();
+  }
+
+  private void sendFBUR() {
+    ByteBuffer buf = new ByteBuffer(0);
+    buf.putMetadata("incremental", true);
+    pushDataToPad(FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, buf);
+  }
+
+  public String toString() {
+    return "VNCMessageHandler(" + id + ")";
+  }
+
+  /**
+   * 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");
+
+    Element source = new MockSource("source") {
+      {
+        // Split messages at random boundaries to check "pushback" logic
+        bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+            // Message type: server bell
+            RfbConstants.SERVER_BELL,
+
+            // Message type: clipboard text
+            RfbConstants.SERVER_CUT_TEXT,
+            // Padding
+            0, 0, 0,
+            // Length (test)
+            0, 0, 0, 4,
+
+        }, new byte[] {
+            // Clipboard text
+            't', 'e', 's', 't',
+
+            // Message type: frame buffer update
+            RfbConstants.SERVER_FRAMEBUFFER_UPDATE,
+            // Padding
+            0,
+            // Number of rectangles
+            0, 3, },
+
+        new byte[] {
+
+            // x, y, width, height: 0x0@4x4
+            0, 0, 0, 0, 0, 4, 0, 4,
+            // Encoding: desktop size
+            (byte) ((RfbConstants.ENCODING_DESKTOP_SIZE >> 24) & 0xff), (byte) ((RfbConstants.ENCODING_DESKTOP_SIZE >> 16) & 0xff),
+            (byte) ((RfbConstants.ENCODING_DESKTOP_SIZE >> 8) & 0xff), (byte) ((RfbConstants.ENCODING_DESKTOP_SIZE >> 0) & 0xff), },
+
+        new byte[] {
+
+            // x, y, width, height: 0x0@4x4
+            0, 0, 0, 0, 0, 4, 0, 4,
+            // Encoding: raw rect
+            (byte) ((RfbConstants.ENCODING_RAW >> 24) & 0xff), (byte) ((RfbConstants.ENCODING_RAW >> 16) & 0xff),
+            (byte) ((RfbConstants.ENCODING_RAW >> 8) & 0xff), (byte) ((RfbConstants.ENCODING_RAW >> 0) & 0xff),
+            // Raw pixel data 4x4x1 bpp
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, }, new byte[] { 11, 12, 13, 14, 15, 16,
+
+            // x, y, width, height: 0x0@2x2
+            0, 0, 0, 0, 0, 2, 0, 2,
+            // Encoding: copy rect
+            (byte) ((RfbConstants.ENCODING_COPY_RECT >> 24) & 0xff), (byte) ((RfbConstants.ENCODING_COPY_RECT >> 16) & 0xff),
+            (byte) ((RfbConstants.ENCODING_COPY_RECT >> 8) & 0xff), (byte) ((RfbConstants.ENCODING_COPY_RECT >> 0) & 0xff),
+            // srcX, srcY: 2x2
+            0, 2, 0, 2, });
+      }
+    };
+
+    ScreenDescription screen = new ScreenDescription() {
+      {
+        this.bytesPerPixel = 1;
+      }
+    };
+
+    final Element handler = new VncMessageHandler("handler", screen);
+
+    ByteBuffer[] emptyBuf = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {});
+    Element fburSink = new MockSink("fbur", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {}, new byte[] {}));
+    Element bellSink = new MockSink("bell", emptyBuf);
+    Element clipboardSink = new MockSink("clipboard", emptyBuf);
+    Element desktopSizeChangeSink = new MockSink("desktop_size", emptyBuf);
+    Element pixelsSink = new MockSink("pixels",
+        ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, }));
+    Element copyRectSink = new MockSink("copy_rect", emptyBuf);
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.addAndLink(source, handler);
+    pipeline.add(fburSink, bellSink, clipboardSink, desktopSizeChangeSink, pixelsSink, copyRectSink);
+
+    pipeline.link("handler >" + FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, "fbur");
+    pipeline.link("handler >" + SERVER_BELL_ADAPTER_PAD, "bell");
+    pipeline.link("handler >" + SERVER_CLIPBOARD_ADAPTER_PAD, "clipboard");
+    pipeline.link("handler >" + PIXEL_ADAPTER_PAD, "pixels");
+
+    pipeline.runMainLoop("source", STDOUT, false, false);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java
new file mode 100644
index 0000000..52d9976
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java
@@ -0,0 +1,291 @@
+// 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 vncclient;
+
+import java.security.spec.KeySpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.FakeSink;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.OneTimeSwitch;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class Vnc_3_3_Authentication extends OneTimeSwitch {
+
+  /**
+   * Password to use when authentication is required.
+   */
+  protected String password = null;
+
+  /**
+   * Authentication stage:
+   * <ul>
+   * <li>0 - challenge received, response must be sent
+   * <li>1 - authentication result received.
+   * </ul>
+   */
+  protected int stage = 0;
+
+  public Vnc_3_3_Authentication(String id) {
+    super(id);
+  }
+
+  public Vnc_3_3_Authentication(String id, String password) {
+    super(id);
+    this.password = password;
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    switch (stage) {
+    case 0: // Read security with optional challenge and response
+      stage0(buf, link);
+
+      break;
+    case 1: // Read authentication response
+      stage1(buf, link);
+      break;
+    }
+
+  }
+
+  /**
+   * Read security type. If connection type is @see
+   * RfbConstants.CONNECTION_FAILED, then throw exception. If connection type is @see
+   * RfbConstants.NO_AUTH, then switch off this element. If connection type is @see
+   * RfbConstants.VNC_AUTH, then read challenge, send encoded password, and read
+   * authentication response.
+   */
+  private void stage0(ByteBuffer buf, Link link) {
+    // At least 4 bytes are necessary
+    if (!cap(buf, 4, UNLIMITED, link, true))
+      return;
+
+    // Read security type
+    int authType = buf.readSignedInt();
+
+    switch (authType) {
+    case RfbConstants.CONNECTION_FAILED: {
+      // Server forbids to connect. Read reason and throw exception
+
+      int length = buf.readSignedInt();
+      String reason = new String(buf.data, buf.offset, length, RfbConstants.US_ASCII_CHARSET);
+
+      throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason);
+    }
+
+    case RfbConstants.NO_AUTH: {
+      // Client can connect without authorization. Nothing to do.
+      // Switch off this element from circuit
+      switchOff();
+      break;
+    }
+
+    case RfbConstants.VNC_AUTH: {
+      // Read challenge and generate response
+      responseToChallenge(buf, link);
+      break;
+    }
+
+    default:
+      throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
+    }
+
+  }
+
+  private void responseToChallenge(ByteBuffer buf, Link link) {
+    // Challenge is exactly 16 bytes long
+    if (!cap(buf, 16, 16, link, true))
+      return;
+
+    ByteBuffer challenge = buf.slice(buf.cursor, 16, true);
+    buf.unref();
+
+    // Encode challenge with password
+    ByteBuffer response;
+    try {
+      response = encodePassword(challenge, password);
+      challenge.unref();
+    } catch (Exception e) {
+      throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage());
+    }
+
+    if (verbose) {
+      response.putMetadata("sender", this);
+    }
+
+    // Send encoded challenge
+    nextStage();
+    pushDataToOTOut(response);
+
+  }
+
+  private void nextStage() {
+    stage++;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Next stage: " + stage + ".");
+  }
+
+  /**
+   * Encode password using DES encryption with given challenge.
+   * 
+   * @param challenge
+   *          a random set of bytes.
+   * @param password
+   *          a password
+   * @return DES hash of password and challenge
+   */
+  public ByteBuffer encodePassword(ByteBuffer challenge, String password) {
+    if (challenge.length != 16)
+      throw new RuntimeException("Challenge must be exactly 16 bytes long.");
+
+    // VNC password consist of up to eight ASCII characters.
+    byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding
+    byte[] passwordAsciiBytes = password.getBytes(RfbConstants.US_ASCII_CHARSET);
+    System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8));
+
+    // Flip bytes (reverse bits) in key
+    for (int i = 0; i < key.length; i++) {
+      key[i] = flipByte(key[i]);
+    }
+
+    try {
+      KeySpec desKeySpec = new DESKeySpec(key);
+      SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
+      SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
+      Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
+      cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+      ByteBuffer buf = new ByteBuffer(cipher.doFinal(challenge.data, challenge.offset, challenge.length));
+
+      return buf;
+    } catch (Exception e) {
+      throw new RuntimeException("Cannot encode password.", e);
+    }
+  }
+
+  /**
+   * Reverse bits in byte, so least significant bit will be most significant
+   * bit. E.g. 01001100 will become 00110010.
+   * 
+   * See also: http://www.vidarholen.net/contents/junk/vnc.html ,
+   * http://bytecrafter .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html
+   * 
+   * @param b
+   *          a byte
+   * @return byte in reverse order
+   */
+  private static byte flipByte(byte b) {
+    int b1_8 = (b & 0x1) << 7;
+    int b2_7 = (b & 0x2) << 5;
+    int b3_6 = (b & 0x4) << 3;
+    int b4_5 = (b & 0x8) << 1;
+    int b5_4 = (b & 0x10) >>> 1;
+    int b6_3 = (b & 0x20) >>> 3;
+    int b7_2 = (b & 0x40) >>> 5;
+    int b8_1 = (b & 0x80) >>> 7;
+    byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1);
+    return c;
+  }
+
+  /**
+   * Read authentication result, send nothing.
+   */
+  private void stage1(ByteBuffer buf, Link link) {
+    // Read authentication response
+    if (!cap(buf, 4, 4, link, false))
+      return;
+
+    int authResult = buf.readSignedInt();
+
+    switch (authResult) {
+    case RfbConstants.VNC_AUTH_OK: {
+      // Nothing to do
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Authentication successfull.");
+      break;
+    }
+
+    case RfbConstants.VNC_AUTH_TOO_MANY:
+      throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
+
+    case RfbConstants.VNC_AUTH_FAILED:
+      throw new RuntimeException("Connection to VNC server failed: wrong password.");
+
+    default:
+      throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
+    }
+
+    switchOff();
+
+  }
+
+  public String toString() {
+    return "VNC3.3 Authentication(" + id + ")";
+  }
+
+  /**
+   * 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");
+
+    final String password = "test";
+
+    Element source = new MockSource("source") {
+      {
+        bufs = ByteBuffer.convertByteArraysToByteBuffers(
+        // Request authentication and send 16 byte challenge
+            new byte[] { 0, 0, 0, RfbConstants.VNC_AUTH, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
+            // Respond to challenge with AUTH_OK
+            new byte[] { 0, 0, 0, RfbConstants.VNC_AUTH_OK });
+      }
+    };
+
+    Element mainSink = new FakeSink("mainSink");
+    final Vnc_3_3_Authentication auth = new Vnc_3_3_Authentication("auth", password);
+    Element initSink = new MockSink("initSink") {
+      {
+        // Expect encoded password
+        bufs = new ByteBuffer[] { auth.encodePassword(new ByteBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }), password) };
+      }
+    };
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.addAndLink(source, auth, mainSink);
+    pipeline.add(initSink);
+    pipeline.link("auth >otout", "initSink");
+
+    pipeline.runMainLoop("source", STDOUT, false, false);
+
+  }
+}


[5/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java
new file mode 100644
index 0000000..c85972d
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java
@@ -0,0 +1,33 @@
+// 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.util.HashSet;
+import java.util.Set;
+
+public class RdpState {
+  
+  public long serverShareId;
+  public int serverUserChannelId;
+
+  public Set<Integer> channels=new HashSet<Integer>();
+  
+  public void channelJoined(int actualChannel) {
+    channels.add(actualChannel);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java
new file mode 100644
index 0000000..0e5b79a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java
@@ -0,0 +1,201 @@
+// 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 common.BitmapOrder;
+import common.BitmapRectangle;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.FakeSink;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240624.aspx
+ */
+public class ServerBitmapUpdate extends BaseElement {
+  public static final int UPDATETYPE_BITMAP = 0x0001;
+
+  /**
+   * Indicates that the bitmap data is compressed. The bitmapComprHdr field MUST
+   * be present if the NO_BITMAP_COMPRESSION_HDR (0x0400) flag is not set.
+   */
+  public static final int BITMAP_COMPRESSION = 0x0001;
+
+  /**
+   * Indicates that the bitmapComprHdr field is not present (removed for
+   * bandwidth efficiency to save 8 bytes).
+   */
+  private static final int NO_BITMAP_COMPRESSION_HDR = 0x0400;
+
+  public ServerBitmapUpdate(String id) {
+    super(id);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    // * DEBUG */System.out.println(buf.toHexString(buf.length));
+
+    BitmapOrder order = new BitmapOrder();
+
+    // (2 bytes): A 16-bit, unsigned integer. The update type. This field MUST
+    // be set to UPDATETYPE_BITMAP (0x0001).
+    int updateType = buf.readSignedShortLE();
+    if (updateType != UPDATETYPE_BITMAP)
+      throw new RuntimeException("Unknown update type. Expected update type: UPDATETYPE_BITMAP (0x1). Actual update type: " + updateType + ", buf: " + buf
+          + ".");
+
+    // (2 bytes): A 16-bit, unsigned integer. The number of screen rectangles
+    // present in the rectangles field.
+    int numberRectangles = buf.readSignedShortLE();
+
+    // (variable): Variable-length array of TS_BITMAP_DATA structures, each of
+    // which contains a rectangular clipping taken from the server-side screen
+    // frame buffer. The number of screen clippings in the array is specified by
+    // the numberRectangles field.
+    BitmapRectangle[] rectangles = new BitmapRectangle[numberRectangles];
+    for (int i = 0; i < numberRectangles; i++) {
+      rectangles[i] = readRectangle(buf);
+    }
+    order.rectangles = rectangles;
+
+    buf.assertThatBufferIsFullyRead();
+
+    ByteBuffer data = new ByteBuffer(0);
+    data.setOrder(order);
+    pushDataToAllOuts(data);
+
+    buf.unref();
+  }
+
+  public BitmapRectangle readRectangle(ByteBuffer buf) {
+
+    BitmapRectangle rectangle = new BitmapRectangle();
+
+    // (2 bytes): A 16-bit, unsigned integer. Left bound of the rectangle.
+    rectangle.x = buf.readSignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. Top bound of the rectangle.
+    rectangle.y = buf.readSignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. Inclusive right bound of the
+    // rectangle.
+    int destRight = buf.readSignedShortLE();
+    rectangle.width=destRight-rectangle.x+1;
+
+    // (2 bytes): A 16-bit, unsigned integer. Inclusive bottom bound of the
+    // rectangle.
+    int destBottom = buf.readSignedShortLE();
+    rectangle.height=destBottom-rectangle.y+1;
+
+    // (2 bytes): A 16-bit, unsigned integer. The width of the rectangle.
+    rectangle.bufferWidth = buf.readSignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. The height of the rectangle.
+    rectangle.bufferHeight = buf.readSignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. The color depth of the rectangle
+    // data in bits-per-pixel.
+    rectangle.colorDepth = buf.readSignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. The flags describing the format of
+    // the bitmap data in the bitmapDataStream field.
+    int flags = buf.readSignedShortLE();
+
+    // BITMAP_COMPRESSION 0x0001
+    // Indicates that the bitmap data is compressed. The bitmapComprHdr field
+    // MUST be present if the NO_BITMAP_COMPRESSION_HDR (0x0400) flag is not
+    // set.
+    boolean compressed=((flags & BITMAP_COMPRESSION) > 0);
+
+    // (2 bytes): A 16-bit, unsigned integer. The size in bytes of the data in
+    // the bitmapComprHdr and bitmapDataStream fields.
+    int bitmapLength = buf.readSignedShortLE();
+
+    // NO_BITMAP_COMPRESSION_HDR 0x0400
+    // Indicates that the bitmapComprHdr field is not present (removed for
+    // bandwidth efficiency to save 8 bytes).
+    if (compressed && (flags & NO_BITMAP_COMPRESSION_HDR) == 0) {
+      // (8 bytes): Optional Compressed Data Header structure specifying the
+      // bitmap data in the bitmapDataStream.
+      // This field MUST be present if the BITMAP_COMPRESSION (0x0001) flag is
+      // present in the Flags field, but the NO_BITMAP_COMPRESSION_HDR (0x0400)
+      // flag is not.
+
+      // Note: Even when compression header is enabled, server sends nothing.
+      // rectangle.compressedBitmapHeader = buf.readBytes(8);
+    }
+
+    // (variable): A variable-length array of bytes describing a bitmap image.
+    // Bitmap data is either compressed or uncompressed, depending on whether
+    // the BITMAP_COMPRESSION flag is present in the Flags field. Uncompressed
+    // bitmap data is formatted as a bottom-up, left-to-right series of pixels.
+    // Each pixel is a whole number of bytes. Each row contains a multiple of
+    // four bytes (including up to three bytes of padding, as necessary).
+    // Compressed bitmaps not in 32 bpp format are compressed using Interleaved
+    // RLE and encapsulated in an RLE Compressed Bitmap Stream structure,
+    // while compressed bitmaps at a color depth of 32 bpp are compressed
+    // using RDP 6.0 Bitmap Compression and stored inside
+    // an RDP 6.0 Bitmap Compressed Stream structure.
+    if (!compressed) {
+      rectangle.bitmapDataStream = buf.readBytes(bitmapLength);
+    } else {
+      ByteBuffer compressedImage = buf.readBytes(bitmapLength);
+      //* DEBUG */System.out.println("Compressed image: " + compressedImage + ", depth: " + rectangle.bitsPerPixel + ".");
+      rectangle.bitmapDataStream = RLEBitmapDecompression.rleDecompress(compressedImage, rectangle.bufferWidth, rectangle.bufferHeight, rectangle.colorDepth);
+      compressedImage.unref();
+    }
+
+    return rectangle;
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    ByteBuffer packet = new ByteBuffer(new byte[] { 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00,
+        0x01, 0x04, 0x0a, 0x00, 0x0c, (byte) 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+
+    Element bitmap = new ServerBitmapUpdate("bitmap") {
+      {
+        verbose = true;
+      }
+    };
+    FakeSink fakeSink = new FakeSink("sink") {
+      {
+        verbose = true;
+      }
+    };
+    Pipeline pipeline = new PipelineImpl("test");
+
+    // BufferedImageCanvas canvas = new BufferedImageCanvas(1024, 768);
+    // Element adapter = new AwtRdpAdapter("test",canvas );
+    // pipeline.addAndLink(bitmap, adapter);
+    pipeline.addAndLink(bitmap, fakeSink);
+
+    bitmap.handleData(packet, null);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java
new file mode 100644
index 0000000..fdad522
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java
@@ -0,0 +1,530 @@
+// 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.Element;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class ServerChannel1003Router extends BaseElement {
+
+  /**
+   * Demand Active PDU.
+   */
+  public static final int PDUTYPE_DEMANDACTIVEPDU = 0x1;
+
+  /**
+   * Confirm Active PDU.
+   */
+  public static final int PDUTYPE_CONFIRMACTIVEPDU = 0x3;
+
+  /**
+   * Deactivate All PDU.
+   */
+  public static final int PDUTYPE_DEACTIVATEALLPDU = 0x6;
+
+  /**
+   * Data PDU (actual type is revealed by the pduType2 field in the Share Data
+   * Header).
+   */
+  public static final int PDUTYPE_DATAPDU = 0x7;
+
+  /**
+   * Enhanced Security Server Redirection PDU.
+   */
+  public static final int PDUTYPE_SERVER_REDIR_PKT = 0xA;
+
+  protected RdpState state;
+
+  public ServerChannel1003Router(String id, RdpState state) {
+    super(id);
+    this.state=state;
+  }
+
+  /**
+   * @see http://msdn.microsoft.com/en-us/library/cc240576.aspx
+   */
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    int length = buf.readUnsignedShortLE();
+    if(buf.length!=length)
+    {
+      // It is ServerErrorAlert-ValidClient
+      // Ignore it
+      //throw new RuntimeException("[" + this + "] ERROR: Incorrect PDU length: " + length + ", data: " + buf + ".");
+    }
+
+    int type = buf.readUnsignedShortLE() & 0xf;
+
+    // int sourceId = buf.readUnsignedShortLE();
+    buf.skipBytes(2);
+
+    switch (type) {
+    case PDUTYPE_DEMANDACTIVEPDU:
+      pushDataToPad("demand_active", buf);
+      break;
+    case PDUTYPE_CONFIRMACTIVEPDU:
+      throw new RuntimeException("Unexpected client CONFIRM ACTIVE PDU. Data: " + buf + ".");
+    case PDUTYPE_DEACTIVATEALLPDU:
+      // pushDataToPad("deactivate_all", buf);
+      /* ignore */buf.unref();
+      break;
+    case PDUTYPE_DATAPDU:
+      handleDataPdu(buf);
+      break;
+    case PDUTYPE_SERVER_REDIR_PKT:
+      // pushDataToPad("server_redir", buf);
+      /* ignore */buf.unref();
+      break;
+    default:
+      throw new RuntimeException("[" + this + "] ERROR: Unknown PDU type: " + type + ", data: " + buf + ".");
+    }
+
+  }
+
+  /**
+   * Graphics Update PDU.
+   */
+  public static final int PDUTYPE2_UPDATE = 0x02;
+
+  /**
+   * Control PDU.
+   */
+  public static final int PDUTYPE2_CONTROL = 0x14;
+
+  /**
+   * Pointer Update PDU.
+   */
+  public static final int PDUTYPE2_POINTER = 0x1B;
+
+  /**
+   * Input Event PDU.
+   */
+  public static final int PDUTYPE2_INPUT = 0x1C;
+
+  /**
+   * Synchronize PDU.
+   */
+  public static final int PDUTYPE2_SYNCHRONIZE = 0x1F;
+
+  /**
+   * Refresh Rect PDU.
+   */
+  public static final int PDUTYPE2_REFRESH_RECT = 0x21;
+
+  /**
+   * Play Sound PDU.
+   */
+  public static final int PDUTYPE2_PLAY_SOUND = 0x22;
+
+  /**
+   * Suppress Output PDU.
+   */
+  public static final int PDUTYPE2_SUPPRESS_OUTPUT = 0x23;
+
+  /**
+   * Shutdown Request PDU.
+   */
+  public static final int PDUTYPE2_SHUTDOWN_REQUEST = 0x24;
+
+  /**
+   * Shutdown Request Denied PDU.
+   */
+  public static final int PDUTYPE2_SHUTDOWN_DENIED = 0x25;
+
+  /**
+   * Save Session Info PDU.
+   */
+  public static final int PDUTYPE2_SAVE_SESSION_INFO = 0x26;
+
+  /**
+   * Font List PDU.
+   */
+  public static final int PDUTYPE2_FONTLIST = 0x27;
+
+  /**
+   * Font Map PDU.
+   */
+  public static final int PDUTYPE2_FONTMAP = 0x28;
+
+  /**
+   * Set Keyboard Indicators PDU.
+   */
+  public static final int PDUTYPE2_SET_KEYBOARD_INDICATORS = 0x29;
+
+  /**
+   * Persistent Key List PDU.
+   */
+  public static final int PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST = 0x2B;
+
+  /**
+   * Bitmap Cache Error PDU.
+   */
+  public static final int PDUTYPE2_BITMAPCACHE_ERROR_PDU = 0x2C;
+
+  /**
+   * Set Keyboard IME Status PDU.
+   */
+  public static final int PDUTYPE2_SET_KEYBOARD_IME_STATUS = 0x2D;
+
+  /**
+   * Offscreen Bitmap Cache Error PDU.
+   */
+  public static final int PDUTYPE2_OFFSCRCACHE_ERROR_PDU = 0x2E;
+
+  /**
+   * Set Error Info PDU.
+   */
+  public static final int PDUTYPE2_SET_ERROR_INFO_PDU = 0x2F;
+
+  /**
+   * DrawNineGrid Cache Error PDU.
+   */
+  public static final int PDUTYPE2_DRAWNINEGRID_ERROR_PDU = 0x30;
+
+  /**
+   * GDI+ Error PDU.
+   */
+  public static final int PDUTYPE2_DRAWGDIPLUS_ERROR_PDU = 0x31;
+
+  /**
+   * Auto-Reconnect Status PDU.
+   */
+  public static final int PDUTYPE2_ARC_STATUS_PDU = 0x32;
+
+  /**
+   * Status Info PDU.
+   */
+  public static final int PDUTYPE2_STATUS_INFO_PDU = 0x36;
+
+  /**
+   * Monitor Layout PDU.
+   */
+  public static final int PDUTYPE2_MONITOR_LAYOUT_PDU = 0x37;
+
+  /**
+   * Indicates an Orders Update.
+   */
+  public static final int UPDATETYPE_ORDERS = 0x0000;
+
+  /**
+   * Indicates a Bitmap Graphics Update.
+   */
+  public static final int UPDATETYPE_BITMAP = 0x0001;
+
+  /**
+   * Indicates a Palette Update.
+   */
+  public static final int UPDATETYPE_PALETTE = 0x0002;
+
+  /**
+   * Indicates a Synchronize Update.
+   */
+  public static final int UPDATETYPE_SYNCHRONIZE = 0x0003;
+
+  /**
+   * @see http://msdn.microsoft.com/en-us/library/cc240577.aspx
+   */
+  protected void handleDataPdu(ByteBuffer buf) {
+
+    // (4 bytes): A 32-bit, unsigned integer. Share identifier for the packet.
+     long shareId = buf.readUnsignedIntLE();
+     if(shareId!=state.serverShareId)
+       throw new RuntimeException("Unexpected share ID: "+shareId+".");
+//    buf.skipBytes(4);
+
+    // Padding.
+    buf.skipBytes(1);
+
+    // (1 byte): An 8-bit, unsigned integer. The stream identifier for the
+    // packet.
+    // int streamId = buf.readUnsignedByte();
+    buf.skipBytes(1);
+
+    // (2 bytes): A 16-bit, unsigned integer. The uncompressed length of the
+    // packet in bytes.
+    int uncompressedLength = buf.readUnsignedShortLE();
+
+    // (1 byte): An 8-bit, unsigned integer. The type of Data PDU.
+    int type2 = buf.readUnsignedByte();
+
+    // (1 byte): An 8-bit, unsigned integer. The compression type and flags
+    // specifying the data following the Share Data Header
+    int compressedType = buf.readUnsignedByte();
+    if (compressedType != 0)
+      throw new RuntimeException("Compression of protocol packets is not supported. Data: " + buf + ".");
+
+    // (2 bytes): A 16-bit, unsigned integer. The compressed length of the
+    // packet in bytes.
+    int compressedLength = buf.readUnsignedShortLE();
+    if (compressedLength != 0)
+      throw new RuntimeException("Compression of protocol packets is not supported. Data: " + buf + ".");
+
+    ByteBuffer data = buf.readBytes(uncompressedLength-18);
+    buf.unref();
+
+    switch (type2) {
+
+    case PDUTYPE2_UPDATE: {
+
+      // (2 bytes): A 16-bit, unsigned integer. Type of the graphics update.
+      int updateType = data.readUnsignedShortLE();
+      ByteBuffer data2 = data.readBytes(data.length - data.cursor);
+      data.unref();
+
+      switch (updateType) {
+      case UPDATETYPE_ORDERS:
+        pushDataToPad("orders", data2);
+        break;
+      case UPDATETYPE_BITMAP:
+        pushDataToPad("bitmap", data2);
+        break;
+      case UPDATETYPE_PALETTE:
+        pushDataToPad("palette", data2);
+        break;
+      case UPDATETYPE_SYNCHRONIZE:
+        // Ignore
+        data2.unref();
+        break;
+      }
+
+      break;
+    }
+    case PDUTYPE2_CONTROL:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_CONTROL ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_POINTER:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_POINTER ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_INPUT:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_INPUT ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SYNCHRONIZE:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SYNCHRONIZE ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_REFRESH_RECT:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_REFRESH_RECT ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_PLAY_SOUND:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_PLAY_SOUND ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SUPPRESS_OUTPUT:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SUPPRESS_OUTPUT ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SHUTDOWN_REQUEST:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SHUTDOWN_REQUEST ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SHUTDOWN_DENIED:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SHUTDOWN_DENIED ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SAVE_SESSION_INFO:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SAVE_SESSION_INFO ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_FONTLIST:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_FONTLIST ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_FONTMAP:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_FONTMAP ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SET_KEYBOARD_INDICATORS:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_KEYBOARD_INDICATORS ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_BITMAPCACHE_ERROR_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_BITMAPCACHE_ERROR_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SET_KEYBOARD_IME_STATUS:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_KEYBOARD_IME_STATUS ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_OFFSCRCACHE_ERROR_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_OFFSCRCACHE_ERROR_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_SET_ERROR_INFO_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_ERROR_INFO_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_DRAWNINEGRID_ERROR_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_DRAWNINEGRID_ERROR_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_DRAWGDIPLUS_ERROR_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_DRAWGDIPLUS_ERROR_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_ARC_STATUS_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_ARC_STATUS_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_STATUS_INFO_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_STATUS_INFO_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+    case PDUTYPE2_MONITOR_LAYOUT_PDU:
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Packet PDUTYPE2_MONITOR_LAYOUT_PDU ignored.");
+      // Ignore
+      data.unref();
+      break;
+
+    default:
+      throw new RuntimeException("Unknow data PDU type: " + type2 + ", data: " + buf + ".");
+    }
+  }
+
+  /**
+   * 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[] {
+        // TPKT
+        (byte) 0x03, (byte) 0x00, // TPKT Header: TPKT version = 3
+        (byte) 0x00, (byte) 0x1B, // TPKT length: 27 bytes
+
+        // X224
+        (byte) 0x02, // X224 Length: 2 bytes
+        (byte) 0xF0, // X224 Type: Data
+        (byte) 0x80, // X224 EOT
+
+        // MCS
+        // Type: send data indication: 26 (0x1a, top 6 bits)
+        (byte) 0x68, // ??
+
+        (byte) 0x00, (byte) 0x01, // User ID: 1002 (1001+1)
+        (byte) 0x03, (byte) 0xEB, // Channel ID: 1003
+        (byte) 0x70, // Data priority: high, segmentation: begin|end
+        (byte) 0x0D, // Payload length: 13 bytes
+
+        // Deactivate all PDU
+        (byte) 0x0D, (byte) 0x00, // Length: 13 bytes (LE)
+
+        // - PDUType: (0x16, LE)
+        // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU
+        // ProtocolVersion: (000000000001....) 1
+        (byte) 0x16, (byte) 0x00,
+
+        (byte) 0xEA, (byte) 0x03, // PDU source: 1002 (LE)
+        (byte) 0xEA, (byte) 0x03, (byte) 0x01, (byte) 0x00, // ShareID = 66538
+
+        (byte) 0x01, (byte) 0x00, // Length if source descriptor: 1 (LE)
+        (byte) 0x00, // Source descriptor (should be set to 0): 0
+    };
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    RdpState rdpState =new RdpState() {{serverShareId=66538;}};
+    Element channel1003 = new ServerChannel1003Router("channel_1003", rdpState );
+    Element mcs = new ServerMCSPDU("mcs");
+    Element tpkt = new ServerTpkt("tpkt");
+    Element x224 = new ServerX224DataPdu("x224");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+        // Deactivate all PDU
+        (byte) 0x0D, (byte) 0x00, // Length: 13 bytes (LE)
+
+        // - PDUType: 22 (0x16, LE)
+        // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU
+        // ProtocolVersion: (000000000001....) 1
+        (byte) 0x16, (byte) 0x00,
+
+        (byte) 0xEA, (byte) 0x03, // PDU source: 1002 (LE)
+        (byte) 0xEA, (byte) 0x03, (byte) 0x01, (byte) 0x00, // ShareID = 66538
+
+        (byte) 0x01, (byte) 0x00, // Length if source descriptor: 1 (LE)
+        (byte) 0x00, // Source descriptor (should be set to 0): 0
+    }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, tpkt, x224, mcs, channel1003, sink);
+    pipeline.link("source", "tpkt", "x224", "mcs >channel_1003", "channel_1003 >deactivate_all", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java
new file mode 100644
index 0000000..f2d3d36
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java
@@ -0,0 +1,117 @@
+// 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.Link;
+import streamer.OneTimeSwitch;
+
+public class ServerControlPDUCooperate  extends OneTimeSwitch {
+
+  public ServerControlPDUCooperate(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 + ".");
+    
+    // Ignore packet
+    buf.unref();
+    switchOff();
+  }
+
+}
+
+/* @formatter:off */
+/*
+03 00 00 28 02 F0 80 68 00 01 03 EB 70 1A 1A 00 17 00 EA 03 EA 03 01 00 9A 02 1A 00 14 00 00 00 04 00 00 00 00 00 00 00 
+
+
+  Frame: Number = 38, Captured Frame Length = 97, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 40
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 40 (0x28)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: Data Packet
+  - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003
+   - Type: Send Data Indication
+    - RootIndex: 26
+       Value: (011010..) 0x1a
+   - UserID: 0x3ea
+    - UserID: 0x3ea
+     - ChannelId: 1002
+      - Align: No Padding
+         Padding2: (00......) 0x0
+        Value: 1 (0x1)
+   - Channel: 0x3eb
+    - ChannelId: 1003
+       Align: No Padding
+       Value: 1003 (0x3EB)
+   - DataPriority: high
+    - DataPriority: high
+     - RootIndex: 1
+        Value: (01......) 0x1
+   - Segmentation: Begin End
+      Begin: (1.......) Begin
+      End:   (.1......) End
+   - Length: 26
+    - Align: No Padding
+       Padding4: (0000....) 0x0
+      Length: 26
+    RDP: RDPBCGR
+- RDPBCGR: TsControlPDU
+  - SlowPathPacket: TsControlPDU 
+   - SlowPath: Type = TS_PDUTYPE_DATAPDU
+    - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU
+       TotalLength: 26 (0x1A)
+     - PDUType: 23 (0x17)
+        Type:            (............0111) TS_PDUTYPE_DATAPDU
+        ProtocolVersion: (000000000001....) 1
+       PDUSource: 1002 (0x3EA)
+    - SlowPathIoPacket: 0x0
+     - ShareDataHeader: TS_PDUTYPE2_CONTROL
+        ShareID: 66538 (0x103EA)
+        Pad1: 154 (0x9A)
+        StreamID: TS_STREAM_MED
+        UncompressedLength: 26 (0x1A)
+        PDUType2: TS_PDUTYPE2_CONTROL
+      - CompressedType: Not Compressed
+         MPPC:       (....0000) MPPC 8K
+         Reserved:   (...0....)
+         Compressed: (..0.....) Not Compressed
+         Front:      (.0......) Not At Front
+         Flush:      (0.......) Not Flushed
+        CompressedLength: 0 (0x0)
+     - TsControlPDU: Action = Cooperate
+        Action: Cooperate
+        GrantID: 0 (0x0)
+        ControlID: 0 (0x0)
+
+ */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java
new file mode 100644
index 0000000..e050e8a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java
@@ -0,0 +1,114 @@
+// 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.Link;
+import streamer.OneTimeSwitch;
+
+public class ServerControlPDUGrantedControl  extends OneTimeSwitch {
+
+  public ServerControlPDUGrantedControl(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 + ".");
+    
+    // Ignore packet
+    buf.unref();
+    switchOff();
+  }
+
+}
+/* @formatter:off */
+/*
+03 00 00 28 02 F0 80 68 00 01 03 EB 70 1A 1A 00 17 00 EA 03 EA 03 01 00 50 02 1A 00 14 00 00 00 02 00 EC 03 EA 03 00 00 
+
+  Frame: Number = 45, Captured Frame Length = 97, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 40
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 40 (0x28)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: Data Packet
+  - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003
+   - Type: Send Data Indication
+    - RootIndex: 26
+       Value: (011010..) 0x1a
+   - UserID: 0x3ea
+    - UserID: 0x3ea
+     - ChannelId: 1002
+      - Align: No Padding
+         Padding2: (00......) 0x0
+        Value: 1 (0x1)
+   - Channel: 0x3eb
+    - ChannelId: 1003
+       Align: No Padding
+       Value: 1003 (0x3EB)
+   - DataPriority: high
+    - DataPriority: high
+     - RootIndex: 1
+        Value: (01......) 0x1
+   - Segmentation: Begin End
+      Begin: (1.......) Begin
+      End:   (.1......) End
+   - Length: 26
+    - Align: No Padding
+       Padding4: (0000....) 0x0
+      Length: 26
+    RDP: RDPBCGR
+- RDPBCGR: TsControlPDU
+  - SlowPathPacket: TsControlPDU 
+   - SlowPath: Type = TS_PDUTYPE_DATAPDU
+    - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU
+       TotalLength: 26 (0x1A)
+     - PDUType: 23 (0x17)
+        Type:            (............0111) TS_PDUTYPE_DATAPDU
+        ProtocolVersion: (000000000001....) 1
+       PDUSource: 1002 (0x3EA)
+    - SlowPathIoPacket: 0x0
+     - ShareDataHeader: TS_PDUTYPE2_CONTROL
+        ShareID: 66538 (0x103EA)
+        Pad1: 80 (0x50)
+        StreamID: TS_STREAM_MED
+        UncompressedLength: 26 (0x1A)
+        PDUType2: TS_PDUTYPE2_CONTROL
+      - CompressedType: Not Compressed
+         MPPC:       (....0000) MPPC 8K
+         Reserved:   (...0....)
+         Compressed: (..0.....) Not Compressed
+         Front:      (.0......) Not At Front
+         Flush:      (0.......) Not Flushed
+        CompressedLength: 0 (0x0)
+     - TsControlPDU: Action = Granted Control
+        Action: Granted Control
+        GrantID: 1004 (0x3EC)
+        ControlID: 1002 (0x3EA)
+ */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java
new file mode 100644
index 0000000..9ce87d3
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java
@@ -0,0 +1,661 @@
+// 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.Element;
+import streamer.FakeSink;
+import streamer.Link;
+import streamer.MockSource;
+import streamer.Order;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+import common.ScreenDescription;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240669.aspx
+ * @see http://msdn.microsoft.com/en-us/library/cc240484.aspx
+ */
+public class ServerDemandActivePDU extends BaseElement {
+
+  /**
+   * Demand Active PDU.
+   */
+  public static final int PDUTYPE_DEMANDACTIVEPDU = 0x1;
+
+  protected RdpState state;
+  protected ScreenDescription screen;
+
+  public ServerDemandActivePDU(String id, ScreenDescription screen, RdpState state) {
+    super(id);
+    this.state = state;
+    this.screen = screen;
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    // Total length of packet
+    int length = buf.readSignedShortLE(); // Ignore
+    if (buf.length != length)
+      throw new RuntimeException("Incorrect length of packet. Length: " + length + ", data: " + buf + ".");
+
+    int type = buf.readSignedShortLE() & 0xf;
+    if (type != PDUTYPE_DEMANDACTIVEPDU)
+      throw new RuntimeException("Unknown PDU type. Expected type: Demand Active PDU (0x1), actual tyoe: " + type + ", data: " + buf + ".");
+
+    // TS_SHARECONTROLHEADER::pduSource = 0x03ea (1002)
+    int pduSource = buf.readSignedShortLE();
+    if (pduSource != 1002)
+      throw new RuntimeException("Unexepcted source of demand active PDU. Expected source: 1002, actual source: " + pduSource + ".");
+
+    // (4 bytes): A 32-bit, unsigned integer. The share identifier for the
+    // packet (see [T128] section 8.4.2 for more information regarding share
+    // IDs).
+    long shareId = buf.readUnsignedIntLE();
+    state.serverShareId = shareId;
+
+    // Ignore rest of server data because it is not used by this client.
+    // (2 bytes): A 16-bit, unsigned integer. The size in bytes of the
+    // sourceDescriptor field.
+    int lengthSourceDescriptor = buf.readUnsignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. The combined size in bytes of the
+    // numberCapabilities, pad2Octets, and capabilitySets fields.
+    int lengthCombinedCapabilities = buf.readUnsignedShortLE();
+
+    // (variable): A variable-length array of bytes containing a source
+    // descriptor,
+    // ByteBuffer sourceDescriptor = buf.readBytes(lengthSourceDescriptor);
+    buf.skipBytes(lengthSourceDescriptor);
+
+    // (variable): An array of Capability Set (section 2.2.1.13.1.1.1)
+    // structures. The number of capability sets is specified by the
+    // numberCapabilities field.
+    handleCapabiltySets(buf.readBytes(lengthCombinedCapabilities));
+
+    // (4 bytes): A 32-bit, unsigned integer. The session identifier. This field
+    // is ignored by the client.
+    buf.skipBytes(4);
+
+    /* DEBUG */buf.assertThatBufferIsFullyRead();
+
+    buf.unref();
+
+    sendHandshakePackets();
+  }
+
+  /**
+   * General Capability Set
+   */
+  public static final int CAPSTYPE_GENERAL = 0x0001;
+  /**
+   * Bitmap Capability Set
+   */
+  public static final int CAPSTYPE_BITMAP = 0x0002;
+  /**
+   * Order Capability Set
+   */
+  public static final int CAPSTYPE_ORDER = 0x0003;
+  /**
+   * Revision 1 Bitmap Cache Capability Set
+   */
+  public static final int CAPSTYPE_BITMAPCACHE = 0x0004;
+  /**
+   * Control Capability Set
+   */
+  public static final int CAPSTYPE_CONTROL = 0x0005;
+  /**
+   * Window Activation Capability Set
+   */
+  public static final int CAPSTYPE_ACTIVATION = 0x0007;
+  /**
+   * Pointer Capability Set
+   */
+  public static final int CAPSTYPE_POINTER = 0x0008;
+  /**
+   * Share Capability Set
+   */
+  public static final int CAPSTYPE_SHARE = 0x0009;
+  /**
+   * Color Table Cache Capability Set
+   */
+  public static final int CAPSTYPE_COLORCACHE = 0x000A;
+  /**
+   * Sound Capability Set
+   */
+  public static final int CAPSTYPE_SOUND = 0x000C;
+  /**
+   * Input Capability Set
+   */
+  public static final int CAPSTYPE_INPUT = 0x000D;
+  /**
+   * Font Capability Set
+   */
+  public static final int CAPSTYPE_FONT = 0x000E;
+  /**
+   * Brush Capability Set
+   */
+  public static final int CAPSTYPE_BRUSH = 0x000F;
+  /**
+   * Glyph Cache Capability Set
+   */
+  public static final int CAPSTYPE_GLYPHCACHE = 0x0010;
+  /**
+   * Offscreen Bitmap Cache Capability Set
+   */
+  public static final int CAPSTYPE_OFFSCREENCACHE = 0x0011;
+  /**
+   * Bitmap Cache Host Support Capability Set
+   */
+  public static final int CAPSTYPE_BITMAPCACHE_HOSTSUPPORT = 0x0012;
+  /**
+   * Revision 2 Bitmap Cache Capability Set
+   */
+  public static final int CAPSTYPE_BITMAPCACHE_REV2 = 0x0013;
+  /**
+   * Virtual Channel Capability Set
+   */
+  public static final int CAPSTYPE_VIRTUALCHANNEL = 0x0014;
+  /**
+   * DrawNineGrid Cache Capability Set
+   */
+  public static final int CAPSTYPE_DRAWNINEGRIDCACHE = 0x0015;
+  /**
+   * Draw GDI+ Cache Capability Set
+   */
+  public static final int CAPSTYPE_DRAWGDIPLUS = 0x0016;
+  /**
+   * Remote Programs Capability Set
+   */
+  public static final int CAPSTYPE_RAIL = 0x0017;
+  /**
+   * Window List Capability Set
+   */
+  public static final int CAPSTYPE_WINDOW = 0x0018;
+  /**
+   * Desktop Composition Extension Capability Set
+   */
+  public static final int CAPSETTYPE_COMPDESK = 0x0019;
+  /**
+   * Multifragment Update Capability Set
+   */
+  public static final int CAPSETTYPE_MULTIFRAGMENTUPDATE = 0x001A;
+  /**
+   * Large Pointer Capability Set
+   */
+  public static final int CAPSETTYPE_LARGE_POINTER = 0x001B;
+  /**
+   * Surface Commands Capability Set
+   */
+  public static final int CAPSETTYPE_SURFACE_COMMANDS = 0x001C;
+  /**
+   * Bitmap Codecs Capability Set
+   */
+  public static final int CAPSETTYPE_BITMAP_CODECS = 0x001D;
+  /**
+   * Frame Acknowledge Capability Set
+   */
+  public static final int CAPSSETTYPE_FRAME_ACKNOWLEDGE = 0x001E;
+
+  /**
+   * @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
+   */
+  protected void handleCapabiltySets(ByteBuffer buf) {
+    // (2 bytes): A 16-bit, unsigned integer. The number of capability sets
+    // included in the Demand Active PDU.
+    int numberCapabilities = buf.readSignedShortLE();
+
+    // (2 bytes): Padding.
+    buf.skipBytes(2);
+
+    for (int i = 0; i < numberCapabilities; i++) {
+      // (2 bytes): A 16-bit, unsigned integer. The type identifier of the
+      // capability set.
+      int capabilitySetType = buf.readUnsignedShortLE();
+
+      // (2 bytes): A 16-bit, unsigned integer. The length in bytes of the
+      // capability data, including the size of the capabilitySetType and
+      // lengthCapability fields.
+      int lengthCapability = buf.readUnsignedShortLE();
+
+      // (variable): Capability set data which conforms to the structure of the
+      // type given by the capabilitySetType field.
+      ByteBuffer capabilityData = buf.readBytes(lengthCapability - 4);
+
+      switch (capabilitySetType) {
+      case CAPSTYPE_GENERAL:
+        break;
+      case CAPSTYPE_BITMAP:
+        handleBitmapCapabilities(capabilityData);
+        break;
+      case CAPSTYPE_ORDER:
+        break;
+      case CAPSTYPE_BITMAPCACHE:
+        break;
+      case CAPSTYPE_CONTROL:
+        break;
+      case CAPSTYPE_ACTIVATION:
+        break;
+      case CAPSTYPE_POINTER:
+        break;
+      case CAPSTYPE_SHARE:
+        break;
+      case CAPSTYPE_COLORCACHE:
+        break;
+      case CAPSTYPE_SOUND:
+        break;
+      case CAPSTYPE_INPUT:
+        break;
+      case CAPSTYPE_FONT:
+        break;
+      case CAPSTYPE_BRUSH:
+        break;
+      case CAPSTYPE_GLYPHCACHE:
+        break;
+      case CAPSTYPE_OFFSCREENCACHE:
+        break;
+      case CAPSTYPE_BITMAPCACHE_HOSTSUPPORT:
+        break;
+      case CAPSTYPE_BITMAPCACHE_REV2:
+        break;
+      case CAPSTYPE_VIRTUALCHANNEL:
+        break;
+      case CAPSTYPE_DRAWNINEGRIDCACHE:
+        break;
+      case CAPSTYPE_DRAWGDIPLUS:
+        break;
+      case CAPSTYPE_RAIL:
+        break;
+      case CAPSTYPE_WINDOW:
+        break;
+      case CAPSETTYPE_COMPDESK:
+        break;
+      case CAPSETTYPE_MULTIFRAGMENTUPDATE:
+        break;
+      case CAPSETTYPE_LARGE_POINTER:
+        break;
+      case CAPSETTYPE_SURFACE_COMMANDS:
+        break;
+      case CAPSETTYPE_BITMAP_CODECS:
+        break;
+      case CAPSSETTYPE_FRAME_ACKNOWLEDGE:
+        break;
+      default:
+        // Ignore
+        break;
+      }
+
+      capabilityData.unref();
+    }
+
+    // TODO
+
+    buf.unref();
+  }
+
+  /**
+   * @see http://msdn.microsoft.com/en-us/library/cc240554.aspx
+   */
+  protected void handleBitmapCapabilities(ByteBuffer buf) {
+
+    // (2 bytes): A 16-bit, unsigned integer. The server MUST set this field to
+    // the color depth of the session, while the client SHOULD set this field to
+    // the color depth requested in the Client Core Data (section 2.2.1.3.2).
+    int preferredBitsPerPixel = buf.readUnsignedShortLE();
+    screen.setPixelFormatRGBTrueColor(preferredBitsPerPixel);
+
+    // receive1BitPerPixel (2 bytes): A 16-bit, unsigned integer. Indicates
+    // whether the client can receive 1 bpp. This field is ignored and SHOULD be
+    // set to TRUE (0x0001).
+    buf.skipBytes(2);
+
+    // receive4BitsPerPixel(2 bytes): A 16-bit, unsigned integer. Indicates
+    // whether the client can receive 4 bpp. This field is ignored and SHOULD be
+    // set to TRUE (0x0001).
+    buf.skipBytes(2);
+
+    // receive8BitsPerPixel (2 bytes): A 16-bit, unsigned integer. Indicates
+    // whether the client can receive 8 bpp. This field is ignored and SHOULD be
+    // set to TRUE (0x0001).
+    buf.skipBytes(2);
+
+    // (2 bytes): A 16-bit, unsigned integer. The width of the desktop in the
+    // session.
+    int desktopWidth = buf.readUnsignedShortLE();
+
+    // (2 bytes): A 16-bit, unsigned integer. The height of the desktop in the
+    // session.
+    int desktopHeight = buf.readUnsignedShortLE();
+
+    screen.setFramebufferSize(desktopWidth, desktopHeight);
+
+    // pad2octets (2 bytes): A 16-bit, unsigned integer. Padding. Values in this
+    // field MUST be ignored.
+
+    // desktopResizeFlag (2 bytes): A 16-bit, unsigned integer. Indicates
+    // whether resizing the desktop by using a Deactivation-Reactivation
+    // Sequence is supported.
+
+    // bitmapCompressionFlag (2 bytes): A 16-bit, unsigned integer. Indicates
+    // whether bitmap compression is supported. This field MUST be set to TRUE
+    // (0x0001) because support for compressed bitmaps is required for a
+    // connection to proceed.
+
+    // highColorFlags (1 byte): An 8-bit, unsigned integer. Client support for
+    // 16 bpp color modes. This field is ignored and SHOULD be set to zero.
+
+    // drawingFlags (1 byte): An 8-bit, unsigned integer. Flags describing
+    // support for 32 bpp bitmaps.
+
+    // multipleRectangleSupport (2 bytes): A 16-bit, unsigned integer. Indicates
+    // whether the use of multiple bitmap rectangles is supported in the Bitmap
+    // Update (section 2.2.9.1.1.3.1.2). This field MUST be set to TRUE (0x0001)
+    // because multiple rectangle support is required for a connection to
+    // proceed.
+
+    // pad2octetsB (2 bytes): A 16-bit, unsigned integer. Padding. Values in
+    // this field MUST be ignored.
+  }
+
+  /**
+   * Send all client requests in one hop, to simplify logic.
+   */
+  protected void sendHandshakePackets() {
+    // Send reactivation sequence in bulk
+    pushDataToPad("confirm_active", new ByteBuffer((Order) null));
+  }
+
+  /**
+   * 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[] {
+        0x67, 0x01,  //  TS_SHARECONTROLHEADER::totalLength = 0x0167 = 359 bytes
+        0x11, 0x00,  //  TS_SHARECONTROLHEADER::pduType = 0x0011 0x0011 = 0x0010 | 0x0001  = TS_PROTOCOL_VERSION | PDUTYPE_DEMANDACTIVEPDU
+
+        (byte) 0xea, 0x03,  //  TS_SHARECONTROLHEADER::pduSource = 0x03ea (1002)
+
+        (byte) 0xea, 0x03, 0x01, 0x00,  //  TS_DEMAND_ACTIVE_PDU::shareId
+        0x04, 0x00,  //  TS_DEMAND_ACTIVE_PDU::lengthSourceDescriptor = 4 bytes
+        0x51, 0x01,  //  TS_DEMAND_ACTIVE_PDU::lengthCombinedCapabilities = 0x151 = 337 bytes
+
+        0x52, 0x44, 0x50, 0x00,  //  TS_DEMAND_ACTIVE_PDU::sourceDescriptor = "RDP"
+
+        0x0d, 0x00,  //  TS_DEMAND_ACTIVE_PDU::numberCapabilities = 13
+        0x00, 0x00,  //  TS_DEMAND_ACTIVE_PDU::pad2octets
+
+        //  Share Capability Set (8 bytes)
+        // 0x09, 0x00, 0x08, 0x00, (byte) 0xea, 0x03, (byte) 0xdc, (byte) 0xe2,
+        // 
+        0x09, 0x00,  //  TS_SHARE_CAPABILITYSET::capabilitySetType = CAPSTYPE_SHARE (9)
+        0x08, 0x00,  //  TS_SHARE_CAPABILITYSET::lengthCapability = 8 bytes
+        (byte) 0xea, 0x03,  //  TS_SHARE_CAPABILITYSET::nodeID = 0x03ea (1002)
+        (byte) 0xdc, (byte) 0xe2,  //  TS_SHARE_CAPABILITYSET::pad2octets
+
+        //  General Capability Set (24 bytes)
+        // 0x01, 0x00, 0x18, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x04, 
+        // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 
+        // 
+        0x01, 0x00,  //  TS_GENERAL_CAPABILITYSET::capabilitySetType = CAPSTYPE_GENERAL (1)
+        0x18, 0x00,  //  TS_GENERAL_CAPABILITYSET::lengthCapability = 24 bytes
+
+        0x01, 0x00,  //  TS_GENERAL_CAPABILITYSET::osMajorType = TS_OSMAJORTYPE_WINDOWS (1)
+        0x03, 0x00,  //  TS_GENERAL_CAPABILITYSET::osMinorType = TS_OSMINORTYPE_WINDOWS_NT (3)
+        0x00, 0x02,  //  TS_GENERAL_CAPABILITYSET::protocolVersion = TS_CAPS_PROTOCOLVERSION (0x0200)
+        0x00, 0x00,  //  TS_GENERAL_CAPABILITYSET::pad2octetsA
+        0x00, 0x00,  //  TS_GENERAL_CAPABILITYSET::generalCompressionTypes = 0
+        0x1d, 0x04,  //  TS_GENERAL_CAPABILITYSET::extraFlags = 0x041d = 0x0400 | 0x0010 | 0x0008 | 0x0004 | 0x0001 = NO_BITMAP_COMPRESSION_HDR | ENC_SALTED_CHECKSUM | AUTORECONNECT_SUPPORTED | LONG_CREDENTIALS_SUPPORTED | FASTPATH_OUTPUT_SUPPORTED
+
+        0x00, 0x00,  //  TS_GENERAL_CAPABILITYSET::updateCapabilityFlag = 0
+        0x00, 0x00,  //  TS_GENERAL_CAPABILITYSET::remoteUnshareFlag = 0
+        0x00, 0x00,  //  TS_GENERAL_CAPABILITYSET::generalCompressionLevel = 0
+        0x01,  //  TS_GENERAL_CAPABILITYSET::refreshRectSupport = TRUE
+        0x01,  //  TS_GENERAL_CAPABILITYSET::suppressOutputSupport = TRUE
+
+        // Virtual Channel Capability Set (8 bytes)
+        // 0x14, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 
+        // 
+        0x14, 0x00,  //  TS_VIRTUALCHANNEL_CAPABILITYSET::capabilitySetType = CAPSTYPE_VIRTUALCHANNEL (20)
+        0x08, 0x00,  //  TS_VIRTUALCHANNEL_CAPABILITYSET::lengthCapability = 8 bytes
+
+        0x02, 0x00, 0x00, 0x00,  //  TS_VIRTUALCHANNEL_CAPABILITYSET::vccaps1 = 0x00000002 = VCCAPS_COMPR_CS_8K
+
+        //  DrawGdiPlus Capability Set (40 bytes)
+        // 0x16, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, (byte) 0xf6, 0x13, (byte) 0xf3, 0x01, 0x00, 0x00, 0x00, 
+        // 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, (byte) 0x9c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x61, (byte) 0xa6, (byte) 0x82, (byte) 0x80, 
+        // 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, (byte) 0x91, (byte) 0xbf, 
+        // 
+        0x16, 0x00,  //  TS_DRAW_GDIPLUS_CAPABILITYSET::capabilitySetType = CAPSTYPE_DRAWGDIPLUS (22)
+        0x28, 0x00,  //  TS_DRAW_GDIPLUS_CAPABILITYSET::lengthCapability = 40 bytes
+
+        0x00, 0x00, 0x00, 0x00,  //  TS_DRAW_GDIPLUS_CAPABILITYSET::drawGdiplusSupportLevel = TS_DRAW_GDIPLUS_DEFAULT (0)
+        0x70, (byte) 0xf6, 0x13, (byte) 0xf3,  //  TS_DRAW_GDIPLUS_CAPABILITYSET::GdipVersion (not initialized by server)
+        0x01, 0x00, 0x00, 0x00,  //  TS_DRAW_GDIPLUS_CAPABILITYSET::drawGdiplusCacheLevel  = TS_DRAW_GDIPLUS_CACHE_LEVEL_ONE (1)
+
+        0x01, 0x00,  //  TS_GDIPLUS_CACHE_ENTRIES::GdipGraphicsCacheEntries  (not initialized by server)
+        0x00, 0x00,  //  TS_GDIPLUS_CACHE_ENTRIES::GdipObjectBrushCacheEntries (not initialized by server)
+        0x18, 0x00,  //  TS_GDIPLUS_CACHE_ENTRIES::GdipObjectPenCacheEntries (not initialized by server)
+        0x00, 0x00,  //  TS_GDIPLUS_CACHE_ENTRIES::GdipObjectImageCacheEntries (not initialized by server)
+        (byte) 0x9c, (byte) 0xf6,  //  TS_GDIPLUS_CACHE_ENTRIES::GdipObjectImageAttributesCacheEntries (not initialized by server)
+
+        0x13, (byte) 0xf3,  //  TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipGraphicsCacheChunkSize  (not initialized by server)
+        0x61, (byte) 0xa6,  //  TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectBrushCacheChunkSize (not initialized by server)
+        (byte) 0x82, (byte) 0x80,  //  TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectPenCacheChunkSize (not initialized by server)
+        0x00, 0x00,  //   TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectImageAttributesCacheChunkSize (not initialized by server)
+
+        0x00, 0x00,  //  TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheChunkSize  (not initialized by server)
+        0x00, 0x50,  //  TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheTotalSize  (not initialized by server)
+        (byte) 0x91, (byte) 0xbf,  //  TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheMaxSize (not initialized by server)
+
+        //  Font Capability Set (4 bytes)
+        // 0x0e, 0x00, 0x04, 0x00,
+        //
+        // Due to a bug, the TS_FONT_CAPABILITYSET capability set size is incorrectly set to 4 bytes (it must be 8 bytes). As a result of this bug, the fontSupportFlags and pad2octets fields are missing.
+        0x0e, 0x00,  //  TS_FONT_CAPABILITYSET::capabilitySetType = CAPSTYPE_FONT (14)
+        0x04, 0x00,  //  TS_FONT_CAPABILITYSET::lengthCapability = 4 bytes
+
+
+        //  Bitmap Capability Set (28 bytes)
+        // 0x02, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 
+        // 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
+        // 
+        0x02, 0x00,  //  TS_BITMAP_CAPABILITYSET::capabilitySetType = CAPSTYPE_BITMAP (2)
+        0x1c, 0x00,  //  TS_BITMAP_CAPABILITYSET::lengthCapability = 28 bytes
+
+        0x18, 0x00,  //  TS_BITMAP_CAPABILITYSET::preferredBitsPerPixel = 24 bpp
+        0x01, 0x00,  //  TS_BITMAP_CAPABILITYSET::receive1BitPerPixel = TRUE
+        0x01, 0x00,  //  TS_BITMAP_CAPABILITYSET::receive4BitsPerPixel = TRUE
+        0x01, 0x00,  //  TS_BITMAP_CAPABILITYSET::receive8BitsPerPixel = TRUE
+        0x00, 0x05,  //  TS_BITMAP_CAPABILITYSET::desktopWidth = 1280 pixels
+        0x00, 0x04,  //  TS_BITMAP_CAPABILITYSET::desktopHeight = 1024 pixels
+        0x00, 0x00,  //  TS_BITMAP_CAPABILITYSET::pad2octets
+        0x01, 0x00,  //  TS_BITMAP_CAPABILITYSET::desktopResizeFlag = TRUE
+        0x01, 0x00,  //  TS_BITMAP_CAPABILITYSET::bitmapCompressionFlag = TRUE
+        0x00,  //  TS_BITMAP_CAPABILITYSET::highColorFlags = 0
+        0x00,  //  TS_BITMAP_CAPABILITYSET::pad1octet
+        0x01, 0x00,  //  TS_BITMAP_CAPABILITYSET::multipleRectangleSupport = TRUE
+        0x00, 0x00,  //  TS_BITMAP_CAPABILITYSET::pad2octetsB
+
+        //  Order Capability Set (88 bytes)
+        // 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+        // 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 
+        // 0x00, 0x00, 0x22, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 
+        // 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 
+        // 0x00, 0x00, 0x00, 0x00, (byte) 0xa1, 0x06, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x40, 0x42, 0x0f, 0x00, 
+        // 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+        // 
+        0x03, 0x00,  //  TS_ORDER_CAPABILITYSET::capabilitySetType = CAPSTYPE_ORDER (3)
+        0x58, 0x00,  //  TS_ORDER_CAPABILITYSET::lengthCapability = 88 bytes
+
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // TS_ORDER_CAPABILITYSET::terminalDescriptor = ""
+        0x40, 0x42, 0x0f, 0x00,  //  TS_ORDER_CAPABILITYSET::pad4octetsA
+
+        0x01, 0x00,  //  TS_ORDER_CAPABILITYSET::desktopSaveXGranularity = 1
+        0x14, 0x00,  //  TS_ORDER_CAPABILITYSET::desktopSaveYGranularity = 20
+        0x00, 0x00,  //  TS_ORDER_CAPABILITYSET::pad2octetsA
+        0x01, 0x00,  //  TS_ORDER_CAPABILITYSET::maximumOrderLevel = ORD_LEVEL_1_ORDERS (1)
+        0x00, 0x00,  //  TS_ORDER_CAPABILITYSET::numberFonts = 0
+
+        0x22, 0x00,  //  TS_ORDER_CAPABILITYSET::orderFlags = 0x0022 = 0x0020 | 0x0002 = COLORINDEXSUPPORT | NEGOTIATEORDERSUPPORT   
+
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_DSTBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_PATBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_SCRBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEMBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEM3BLT_INDEX] = TRUE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ATEXTOUT_INDEX] = FALSE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_AEXTTEXTOUT_INDEX] = FALSE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_DRAWNINEGRID_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_LINETO_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTI_DRAWNINEGRID_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_OPAQUERECT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_SAVEBITMAP_INDEX] = TRUE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WTEXTOUT_INDEX] = FALSE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEMBLT_R2_INDEX] = FALSE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEM3BLT_R2_INDEX] = FALSE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIDSTBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIPATBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTISCRBLT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIOPAQUERECT_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_FAST_INDEX_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYGON_SC_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYGON_CB_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYLINE_INDEX] = TRUE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[23] = 0
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_FAST_GLYPH_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ELLIPSE_SC_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ELLIPSE_CB_INDEX] = TRUE
+        0x01,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_INDEX_INDEX] = TRUE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WEXTTEXTOUT_INDEX] = FALSE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WLONGTEXTOUT_INDEX] = FALSE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WLONGEXTTEXTOUT_INDEX] = FALSE
+        0x00,  //  TS_ORDER_CAPABILITYSET::orderSupport[24] = 0
+
+        (byte) 0xa1, 0x06,  //  TS_ORDER_CAPABILITYSET::textFlags = 0x06a1
+
+        0x00, 0x00,  //  TS_ORDER_CAPABILITYSET::pad2octetsB
+        0x40, 0x42, 0x0f, 0x00,  //  TS_ORDER_CAPABILITYSET::pad4octetsB
+
+        0x40, 0x42, 0x0f, 0x00,  //  TS_ORDER_CAPABILITYSET::desktopSaveSize = 0xf4240 = 1000000
+        0x01, 0x00,  //  TS_ORDER_CAPABILITYSET::pad2octetsC
+        0x00, 0x00,  //  TS_ORDER_CAPABILITYSET::pad2octetsD
+        0x00, 0x00,  //  TS_ORDER_CAPABILITYSET::textANSICodePage
+        0x00, 0x00,  //  TS_ORDER_CAPABILITYSET::pad2octetsE
+
+        // Color Table Cache Capability Set (8 bytes)
+        // 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 
+        // 
+        0x0a, 0x00,  //  TS_COLORTABLECACHE_CAPABILITYSET::capabilitySetType = CAPSTYPE_COLORCACHE (10)
+        0x08, 0x00,  //  TS_COLORTABLECACHE_CAPABILITYSET::lengthCapability = 8 bytes
+
+        0x06, 0x00,  //  TS_COLORTABLECACHE_CAPABILITYSET::colorTableCacheSize = 6
+        0x00, 0x00,  //  TS_COLORTABLECACHE_CAPABILITYSET::pad2octets
+
+        // Bitmap Cache Host Support Capability Set (8 bytes)
+        // 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 
+        // 
+        0x12, 0x00,  //  TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::capabilitySetType  = CAPSTYPE_BITMAPCACHE_HOSTSUPPORT (18)
+        0x08, 0x00,  //  TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::lengthCapability  = 8 bytes
+
+        0x01,  //  TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::CacheVersion = 1  (corresponds to rev. 2 capabilities)
+        0x00,  //  TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::Pad1
+        0x00, 0x00,  //  TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::Pad2
+
+        // Pointer Capability Set (10 bytes)
+        // 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, 0x19, 0x00, 
+        // 
+        0x08, 0x00,  //  TS_POINTER_CAPABILITYSET::capabilitySetType = CAPSTYPE_POINTER (8)
+        0x0a, 0x00,  //  TS_POINTER_CAPABILITYSET::lengthCapability = 10 bytes
+
+        0x01, 0x00,  //  TS_POINTER_CAPABILITYSET::colorPointerFlag = TRUE
+        0x19, 0x00,  //  TS_POINTER_CAPABILITYSET::colorPointerCacheSize = 25
+        0x19, 0x00,  //  TS_POINTER_CAPABILITYSET::pointerCacheSize = 25
+
+        //  Input Capability Set (88 bytes)
+        // 0x0d, 0x00, 0x58, 0x00, 0x35, 0x00, 0x00, 0x00, (byte) 0xa1, 0x06, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 
+        // 0x0c, (byte) 0xf6, 0x13, (byte) 0xf3, (byte) 0x93, 0x5a, 0x37, (byte) 0xf3, 0x00, (byte) 0x90, 0x30, (byte) 0xe1, 0x34, 0x1c, 0x38, (byte) 0xf3, 
+        // 0x40, (byte) 0xf6, 0x13, (byte) 0xf3, 0x04, 0x00, 0x00, 0x00, 0x4c, 0x54, (byte) 0xdc, (byte) 0xe2, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 
+        // 0x01, 0x00, 0x00, 0x00, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x00, 0x00, 0x00, 0x00, 0x38, (byte) 0xf6, 0x13, (byte) 0xf3, 
+        // 0x2e, 0x05, 0x38, (byte) 0xf3, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x2c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x00, 0x00, 0x00, 0x00, 
+        // 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00,
+        // 
+        0x0d, 0x00,  //  TS_INPUT_CAPABILITYSET::capabilitySetType = CAPSTYPE_INPUT (13)
+        0x58, 0x00,  //  TS_INPUT_CAPABILITYSET::lengthCapability = 88 bytes
+
+        0x35, 0x00,  //  TS_INPUT_CAPABILITYSET::inputFlags = 0x0035 = 0x0020 | 0x0010 | 0x0004 | 0x0001 = INPUT_FLAG_FASTPATH_INPUT2 | INPUT_FLAG_VKPACKET | INPUT_FLAG_MOUSEX | INPUT_FLAG_SCANCODES
+
+        0x00, 0x00,  //  TS_INPUT_CAPABILITYSET::pad2octetsA
+        (byte) 0xa1, 0x06, 0x00, 0x00,  //  TS_INPUT_CAPABILITYSET::keyboardLayout (not initialized by server)
+        0x40, 0x42, 0x0f, 0x00,  //  TS_INPUT_CAPABILITYSET::keyboardType (not initialized by server)
+        0x0c, (byte) 0xf6, 0x13, (byte) 0xf3,  //  TS_INPUT_CAPABILITYSET::keyboardSubType  (not initialized by server)
+        (byte) 0x93, 0x5a, 0x37, (byte) 0xf3,  //  TS_INPUT_CAPABILITYSET::keyboardFunctionKey (not initialized by server)
+
+        // TS_INPUT_CAPABILITYSET::imeFileName (not initialized by server)
+        0x00, (byte) 0x90, 0x30, (byte) 0xe1, 0x34, 0x1c, 0x38, (byte) 0xf3, 0x40, (byte) 0xf6, 0x13, (byte) 0xf3, 0x04, 0x00, 0x00, 0x00, 
+        0x4c, 0x54, (byte) 0xdc, (byte) 0xe2, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x01, 0x00, 0x00, 0x00, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 
+        0x00, 0x00, 0x00, 0x00, 0x38, (byte) 0xf6, 0x13, (byte) 0xf3, 0x2e, 0x05, 0x38, (byte) 0xf3, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 
+        0x2c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, 
+
+        //  RAIL Capability Set (8 bytes)
+        // 0x17, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 
+        // 
+        0x17, 0x00,  //  TS_RAIL_CAPABILITYSET::capabilitySetType = CAPSTYPE_RAIL (23)
+        0x08, 0x00,  //  TS_RAIL_CAPABILITYSET::lengthCapability = 8 bytes
+
+        0x00, 0x00, 0x00, 0x00,  //  TS_RAIL_CAPABILITYSET::railSupportLevel = TS_RAIL_LEVEL_DEFAULT (0)
+
+        //  Windowing Capability Set (11 bytes)
+        // 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+        //
+        0x18, 0x00,  //  TS_WINDOW_CAPABILITYSET::capabilitySetType =  CAPSTYPE_WINDOW (24)
+        0x0b, 0x00,  //  TS_WINDOW_CAPABILITYSET::lengthCapability = 11 bytes
+
+        0x00, 0x00, 0x00, 0x00,  //  TS_WINDOW_CAPABILITYSET::wndSupportLevel = TS_WINDOW_LEVEL_DEFAULT (0)
+        0x00,  //  TS_WINDOW_CAPABILITYSET::nIconCaches = 0
+        0x00, 0x00,  //  TS_WINDOW_CAPABILITYSET::nIconCacheEntries = 0
+
+        // Remainder of Demand Active PDU:
+
+        0x00, 0x00, 0x00, 0x00,  //  TS_DEMAND_ACTIVE_PDU::sessionId = 0    
+    };
+    /* @formatter:on */
+
+    RdpState rdpState = new RdpState();
+    ScreenDescription screenDescription = new ScreenDescription();
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element demandActive = new ServerDemandActivePDU("demand_active", screenDescription, rdpState);
+    Element sink = new FakeSink("sink");
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, demandActive, sink);
+    pipeline.link("source", "demand_active", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java
new file mode 100644
index 0000000..fbec1ce
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java
@@ -0,0 +1,259 @@
+// 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;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240621.aspx
+ */
+public class ServerFastPath extends BaseElement {
+
+  /**
+   * TPKT protocol version (first byte).
+   */
+  public static final int PROTOCOL_TPKT = 3;
+  
+  /**
+   * Fast path protocol version (first two bits of first byte).
+   */
+  public static final int PROTOCOL_FASTPATH = 0;
+  
+  
+  /**
+   * TPKT packets will be pushed to that pad.
+   */
+  public static final String TPKT_PAD = "tpkt";
+
+  private static final String ORDERS_PAD = "orders";
+  private static final String BITMAP_PAD = "bitmap";
+  private static final String PALETTE_PAD = "palette";
+
+  /**
+   * Indicates that packet contains 8 byte secure checksum at top of packet. Top
+   * two bits of first byte.
+   */
+  public static final int FASTPATH_OUTPUT_SECURE_CHECKSUM = 1;
+
+  /**
+   * Indicates that packet contains 8 byte secure checksum at top of packet and
+   * packet content is encrypted. Top two bits of first byte.
+   */
+  public static final int FASTPATH_OUTPUT_ENCRYPTED = 2;
+
+  public static final int FASTPATH_UPDATETYPE_ORDERS = 0;
+  public static final int FASTPATH_UPDATETYPE_BITMAP = 1;
+  public static final int FASTPATH_UPDATETYPE_PALETTE = 2;
+  public static final int FASTPATH_UPDATETYPE_SYNCHRONIZE = 3;
+  public static final int FASTPATH_UPDATETYPE_SURFCMDS = 4;
+  public static final int FASTPATH_UPDATETYPE_PTR_NULL = 5;
+  public static final int FASTPATH_UPDATETYPE_PTR_DEFAULT = 6;
+  public static final int FASTPATH_UPDATETYPE_PTR_POSITION = 8;
+  public static final int FASTPATH_UPDATETYPE_COLOR = 9;
+  public static final int FASTPATH_UPDATETYPE_CACHED = 0xa;
+  public static final int FASTPATH_UPDATETYPE_POINTER = 0xb;
+
+  public static final int FASTPATH_FRAGMENT_SINGLE = 0;
+  public static final int FASTPATH_FRAGMENT_LAST = 1;
+  public static final int FASTPATH_FRAGMENT_FIRST = 2;
+  public static final int FASTPATH_FRAGMENT_NEXT = 3;
+
+  public static final int FASTPATH_OUTPUT_COMPRESSION_USED = 2;
+
+  public ServerFastPath(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 + ".");
+
+    //* DEBUG */System.out.println(buf.toHexString(buf.length));
+
+    // We need at 4 bytes to read packet type (TPKT or FastPath) and packet
+    // length
+    if (!cap(buf, 4, UNLIMITED, link, false))
+      return;
+
+    int typeAndFlags = buf.readUnsignedByte();
+
+    if (typeAndFlags == PROTOCOL_TPKT) {
+      //
+      // TPKT
+      //
+
+      // Reserved
+      buf.skipBytes(1);
+
+      // Read TPKT length
+      int length = buf.readUnsignedShort();
+
+      if (!cap(buf, length, length, link, false))
+        // Wait for full packet to arrive
+        return;
+
+      pushDataToPad(TPKT_PAD, buf);
+
+      // TPKT is handled
+      return;
+    }
+
+    //
+    // FastPath
+    //
+    // Number of bytes in updateData field (including header (1+1 or 2
+    // bytes))
+    int length = buf.readVariableUnsignedShort();
+
+    // Length is the size of payload, so we need to calculate from cursor
+    if (!cap(buf, length, length, link, false))
+      // Wait for full packet to arrive
+      return;
+
+    int type = typeAndFlags & 0x3;
+    int securityFlags = (typeAndFlags >> 6) & 0x3;
+
+    // Assertions
+    {
+      if (type != PROTOCOL_FASTPATH)
+        throw new RuntimeException("Unknown protocol. Expected protocol: 0 (FastPath). Actual protocol: " + type + ", data: " + buf + ".");
+
+      switch (securityFlags) {
+      case FASTPATH_OUTPUT_SECURE_CHECKSUM:
+        // TODO
+        throw new RuntimeException("Secure checksum is not supported in FastPath packets.");
+      case FASTPATH_OUTPUT_ENCRYPTED:
+        // TODO
+        throw new RuntimeException("Encryption is not supported in FastPath packets.");
+      }
+    }
+
+    // TODO: optional FIPS information, when FIPS is selected
+    // TODO: optional data signature (checksum), when checksum or FIPS is
+    // selected
+
+    // Array of FastPath update fields
+    while (buf.cursor < buf.length) {
+
+      int updateHeader = buf.readUnsignedByte();
+
+      int size = buf.readUnsignedShortLE();
+
+      int updateCode = updateHeader & 0xf;
+      int fragmentation = (updateHeader >> 4) & 0x3;
+      int compression = (updateHeader >> 6) & 0x3;
+
+      if (verbose)
+        System.out.println("[" + this + "] INFO: FastPath update received. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + ", compression: "
+            + compression + ", size: " + size + ".");
+
+      ByteBuffer data = buf.readBytes(size);
+      buf.putMetadata("fragmentation", fragmentation);
+      buf.putMetadata("compression", compression);
+
+      switch (updateCode) {
+
+      case FASTPATH_UPDATETYPE_ORDERS:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_ORDERS.");
+        pushDataToPad(ORDERS_PAD, data);
+        break;
+
+      case FASTPATH_UPDATETYPE_BITMAP:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_BITMAP.");
+        pushDataToPad(BITMAP_PAD, data);
+        break;
+
+      case FASTPATH_UPDATETYPE_PALETTE:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PALETTE.");
+        pushDataToPad(PALETTE_PAD, data);
+        break;
+
+      case FASTPATH_UPDATETYPE_SYNCHRONIZE:
+        // @see http://msdn.microsoft.com/en-us/library/cc240625.aspx
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_SYNCHRONIZE.");
+
+        data.unref();
+
+        if (size != 0)
+          throw new RuntimeException("Size of FastPath synchronize packet must be 0. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation
+              + ", compression: " + compression + ", size: " + size + ", data: " + data + ".");
+        break;
+
+      case FASTPATH_UPDATETYPE_SURFCMDS:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_SURFCMDS.");
+
+        break;
+
+      case FASTPATH_UPDATETYPE_PTR_NULL:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_NULL.");
+
+        break;
+
+      case FASTPATH_UPDATETYPE_PTR_DEFAULT:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_DEFAULT.");
+
+        break;
+
+      case FASTPATH_UPDATETYPE_PTR_POSITION:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_POSITION.");
+
+        break;
+
+      case FASTPATH_UPDATETYPE_COLOR:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_COLOR.");
+
+        break;
+
+      case FASTPATH_UPDATETYPE_CACHED:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_CACHED.");
+
+        break;
+
+      case FASTPATH_UPDATETYPE_POINTER:
+        if (verbose)
+          System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_POINTER.");
+
+        break;
+
+      default:
+        throw new RuntimeException("Unknown FastPath update. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + ", compression: " + compression
+            + ", size: " + size + ", data: " + data + ".");
+
+      }
+
+    }
+
+    buf.unref();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java
new file mode 100644
index 0000000..194ffe6
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java
@@ -0,0 +1,121 @@
+// 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.Link;
+import streamer.OneTimeSwitch;
+
+public class ServerLicenseErrorPDUValidClient extends OneTimeSwitch {
+
+  public ServerLicenseErrorPDUValidClient(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 + ".");
+
+    // Ignore packet
+    buf.unref();
+    switchOff();
+  }
+
+  /* @formatter:off */
+//   * Server Error alert
+//
+//03 00 00 22 02 F0 80 68 00 01 03 EB 70 14 80 00 F1 BC FF 03 10 00 07 00 00 00 02 00 00 00 04 00 00 00
+//
+//
+// Frame: Number = 30, Captured Frame Length = 91, MediaType = DecryptedPayloadHeader
+//+ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+// TLSSSLData: Transport Layer Security (TLS) Payload Data
+//+ TLS: TLS Rec Layer-1 SSL Application Data
+// ISOTS: TPKTCount = 1
+//- TPKT: version: 3, Length: 34
+//    version: 3 (0x3)
+//    Reserved: 0 (0x0)
+//    PacketLength: 34 (0x22)
+//- X224: Data
+//    Length: 2 (0x2)
+//    Type: Data
+//    EOT: 128 (0x80)
+//- T125: Data Packet
+// - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003
+//  - Type: Send Data Indication
+//    - RootIndex: 26
+//      Value: (011010..) 0x1a
+//  - UserID: 0x3ea
+//    - UserID: 0x3ea
+//    - ChannelId: 1002
+//     - Align: No Padding
+//        Padding2: (00......) 0x0
+//       Value: 1 (0x1)
+//  - Channel: 0x3eb
+//    - ChannelId: 1003
+//      Align: No Padding
+//      Value: 1003 (0x3EB)
+//  - DataPriority: high
+//    - DataPriority: high
+//    - RootIndex: 1
+//       Value: (01......) 0x1
+//  - Segmentation: Begin End
+//     Begin: (1.......) Begin
+//     End:   (.1......) End
+//  - Length: 20
+//    - Align: No Padding
+//      Padding4: (0000....) 0x0
+//     Length: 20
+//    RDP: RDPBCGR
+//- RDPBCGR: RDPELE
+// - SecurityHeader: License Packet
+//  - Flags: 128 (0x80)
+//     SecurityExchange:        (...............0) Not Security Exchange PDU
+//     Reserved1:               (.............00.) Reserved
+//     Encrypted:               (............0...) Not Encrypted packet
+//     ResetSeqNumber:          (...........0....) MUST be ignored.
+//     IgnoreSeqNumber:         (..........0.....) MUST be ignored.
+//     InfoPacket:              (.........0......) Not Client Info PDU
+//     LicensePacket:           (........1.......) License Packet
+//     Reserved2:               (.......0........) Reserved
+//     LicensePacketEncryption: (......0.........) Not License Packet Encryption
+//     ServerRedirectionPacket: (.....0..........) Not Standard Security Server Redirection PDU
+//     ImprovedChecksumForMACG: (....0...........) Not Improved Checksum for MAC Generation
+//     Reserved3:               (.000............) Reserved
+//     FlagsHiValid:            (0...............) FlagsHi should be ignored
+//    FlagsHi: Should be ignored
+//- RDPELE: GM_ERROR_ALERT
+// - TsPreambleHeader: Type = GM_ERROR_ALERT
+//    MsgType: GM_ERROR_ALERT
+//  - Flags: 3 (0x3)
+//     LicenseProtocolVersionMask: (....0011) RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0
+//     Unused:                     (.000....)
+//     ExtendedErrorMSGsupported:  (0.......) that extended error information using the License Error Message is NOT supported.
+//    MsgSize: 16 (0x10)
+// - TsLicenseErrorMessage: ErrorCode = STATUS_VALID_CLIENT
+//    ErrorCode: STATUS_VALID_CLIENT
+//    StateTransition: ST_NO_TRANSITION
+//  - LiceseBinaryBlob: Type = Not Available
+//     RandomData: This value should be ignored
+//     BlobLen: 0 (0x0)
+//
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java
new file mode 100644
index 0000000..8373b83
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java
@@ -0,0 +1,133 @@
+// 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;
+
+/**
+ * Server response to MCS Attach User request.
+ * 
+ * Once the User Channel ID has been extracted, the client MUST send an MCS
+ * Channel Join Request PDU for the user channel.
+ * 
+ * @see http://msdn.microsoft.com/en-us/library/cc240685.aspx
+ */
+public class ServerMCSAttachUserConfirmPDU extends OneTimeSwitch {
+
+  public static final int MCS_ATTACH_USER_CONFIRM_PDU = 0xb;
+
+  public static final int INITIATOR_PRESENT = 0x2;
+
+  protected RdpState state;
+
+  public ServerMCSAttachUserConfirmPDU(String id, RdpState state) {
+    super(id);
+    this.state = state;
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    int typeAndFlags = buf.readUnsignedByte();
+    int type = typeAndFlags >> 2;
+    int flags = typeAndFlags & 0x3;
+    
+    if (type != MCS_ATTACH_USER_CONFIRM_PDU)
+      throw new RuntimeException("["+this+"] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 11, actual value: " + type + ", data: " + buf + ".");
+
+    if (flags != INITIATOR_PRESENT)
+      throw new RuntimeException("Initator field is not present in MCS AttachUserConfirm PDU. Data: " + buf + ".");
+
+    int rtSuccess = buf.readUnsignedByte() >> 4;
+    if (rtSuccess != 0)
+      throw new RuntimeException("["+this+"] ERROR: Cannot attach user: request failed. Error code: " + rtSuccess + ", data: " + buf + ".");
+
+    // If the initiator field is present, the client stores the value of the
+    // initiator in the User Channel ID store , because the initiator specifies
+    // the User Channel ID.
+    state.serverUserChannelId = buf.readUnsignedShort() + 1001;
+
+    buf.unref();
+
+    // Next: client MCS Channel Join Request PDU (s)
+    switchOff();
+  }
+
+  /**
+   * Example.
+   */
+  /**
+   * Example.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc240842.aspx
+   * @see http://msdn.microsoft.com/en-us/library/cc240500.aspx
+   */
+  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[] { (byte) 0x2E, // MCS user confirm (001011..,
+                                              // 0xb), InitiatorPresent: 1
+                                              // (......01, 0x1)
+        (byte) 0x00, // RT successfull (0000...., 0x0)
+        // Initiator: 1001+3 = 1004
+        (byte) 0x00, (byte) 0x03, };
+
+    RdpState rdpState = new RdpState();
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet, new byte[] { 1, 2, 3 }));
+    Element atachUserConfirm = new ServerMCSAttachUserConfirmPDU("attach_user_confirm", rdpState);
+    Element sink = new MockSink("sink");
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, atachUserConfirm, sink, mainSink);
+    pipeline.link("source", "attach_user_confirm", "mainSink");
+    pipeline.link("attach_user_confirm >" + OTOUT, "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+
+    if (rdpState.serverUserChannelId != 1004)
+      System.err.println("Incorrect user channel ID. Expected value: 1004, actual value: " + rdpState.serverUserChannelId + ".");
+  }
+
+}
+
+/*
+ * 03 00 00 0B 02 F0 80 2E 00 00 03.
+ * 
+ * Frame: Number = 18, Captured Frame Length = 68, MediaType =
+ * DecryptedPayloadHeader + DecryptedPayloadHeader: FrameCount = 1, ErrorStatus
+ * = SUCCESS TLSSSLData: Transport Layer Security (TLS) Payload Data + TLS: TLS
+ * Rec Layer-1 SSL Application Data ISOTS: TPKTCount = 1 - TPKT: version: 3,
+ * Length: 11 version: 3 (0x3) Reserved: 0 (0x0) PacketLength: 11 (0xB) - X224:
+ * Data Length: 2 (0x2) Type: Data EOT: 128 (0x80) - T125: Attach User Confirm,
+ * Result = rt-successful, Indicator = 0x3ec - MCSHeader: Type=Attach User
+ * Confirm - Type: Attach User Confirm - RootIndex: 11 Value: (001011..) 0xb -
+ * MCSAttachUserConfirm: Result = rt-successful, Indicator = 0x3ec
+ * InitiatorPresent: 1 (0x1) - Result: rt-successful - Result: rt-successful -
+ * RootIndex: 0 Value: (0000....) 0x0 - Initiator: 0x3ec - UserID: 0x3ec -
+ * ChannelId: 1004 - Align: No Padding Padding5: (00000...) 0x0 Value: 3 (0x3)
+ */


[4/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java
new file mode 100644
index 0000000..d0a8e81
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java
@@ -0,0 +1,89 @@
+// 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.Link;
+import streamer.OneTimeSwitch;
+
+public class ServerMCSChannelJoinConfirmPDU  extends OneTimeSwitch {
+
+  protected int channel;
+
+  public ServerMCSChannelJoinConfirmPDU(String id, int channel) {
+    super(id);
+    this.channel=channel;
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+    
+    // Ignore packet
+    buf.unref();
+    switchOff();
+  }
+
+}
+
+/*
+ * 03 00 00 0F 02 F0 80 3E 00 00 03 03 EC 03 EC 
+
+  Frame: Number = 22, Captured Frame Length = 72, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 15
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 15 (0xF)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: Channel Join Confirm, ChannelId = 1004, Result = rt-successful
+  - MCSHeader: Type=Channel Join Confirm
+   - Type: Channel Join Confirm
+    - RootIndex: 15
+       Value: (001111..) 0xf
+  - MCSChannelJoinConfirm: ChannelId = 1004, Result = rt-successful
+     ChannelIdPresent: 1 (0x1)
+   - Result: rt-successful
+    - Result: rt-successful
+     - RootIndex: 0
+        Value: (0000....) 0x0
+   - Initiator: 0x3ec
+    - UserID: 0x3ec
+     - ChannelId: 1004
+      - Align: No Padding
+         Padding5: (00000...) 0x0
+        Value: 3 (0x3)
+   - Requested: 0x3ec
+    - ChannelId: 1004
+       Align: No Padding
+       Value: 1004 (0x3EC)
+   - ChannelId: 0x3ec
+    - ChannelId: 1004
+       Align: No Padding
+       Value: 1004 (0x3EC)
+ 
+ */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java
new file mode 100644
index 0000000..30b196b
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java
@@ -0,0 +1,283 @@
+// 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.Link;
+import streamer.OneTimeSwitch;
+
+/**
+ * Once the basic server settings data blocks have been processed successfully, the client MUST send the MCS Attach User Request PDU to the server.
+ * 
+ * @see http://msdn.microsoft.com/en-us/library/cc240682.aspx
+ */
+public class ServerMCSConnectResponse  extends OneTimeSwitch {
+
+  public ServerMCSConnectResponse(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 + ".");
+    
+    // Ignore packet
+    buf.unref();
+    switchOff();
+  }
+
+}
+
+/*
+ * @formatter:off
+ * 03 00 00 64 02 F0 80 7F 66 5A 0A 01 00 02 01 00 30 1A 02 01 22 02 01 03 02 01 00 02 01 01 02 01 00 02 01 01 02 03 00 FF F8 02 01 02 04 36 00 05 00 14 7C 00 01 2A 14 76 0A 01 01 00 01 C0 00 4D 63 44 6E 20 01 0C 0C 00 04 00 08 00 01 00 00 00 03 0C 08 00 EB 03 00 00 02 0C 0C 00 00 00 00 00 00 00 00 00 
+
+  Frame: Number = 12, Captured Frame Length = 157, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 100
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 100 (0x64)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: MCSConnect Response
+  - MCSConnectResponse: Result = rt-successful
+   - ConnectResponseHeader: 
+    - AsnId: Application Constructed Tag (102)
+     - HighTag: 
+        Class:     (01......) Application (1)
+        Type:      (..1.....) Constructed
+        TagNumber: (...11111)
+        TagValueEnd: 102 (0x66)
+    - AsnLen: Length = 90, LengthOfLength = 0
+       Length: 90 bytes, LengthOfLength = 0
+   - Result: rt-successful
+    - Value: 0
+     - AsnIntegerHeader: 
+      - AsnId: Enumerated type (Universal 10)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...01010) 10
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 0 (0x0)
+   - CalledConnectId: 0
+    - AsnIntegerHeader: 
+     - AsnId: Integer type (Universal 2)
+      - LowTag: 
+         Class:    (00......) Universal (0)
+         Type:     (..0.....) Primitive
+         TagValue: (...00010) 2
+     - AsnLen: Length = 1, LengthOfLength = 0
+        Length: 1 bytes, LengthOfLength = 0
+      AsnInt: 0 (0x0)
+   - DomainParameters: Length = 26, LengthOfLength = 0
+    - DomainParametersHeader: 0x1
+     - AsnId: Sequence and SequenceOf types (Universal 16)
+      - LowTag: 
+         Class:    (00......) Universal (0)
+         Type:     (..1.....) Constructed
+         TagValue: (...10000) 16
+     - AsnLen: Length = 26, LengthOfLength = 0
+        Length: 26 bytes, LengthOfLength = 0
+    - ChannelIds: 34
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 34 (0x22)
+    - UserIDs: 3
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 3 (0x3)
+    - TokenIds: 0
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 0 (0x0)
+    - NumPriorities: 1
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 1 (0x1)
+    - MinThroughput: 0
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 0 (0x0)
+    - Height: 1
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 1 (0x1)
+    - MCSPDUsize: 65528
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 3, LengthOfLength = 0
+         Length: 3 bytes, LengthOfLength = 0
+       AsnInt: 65528 (0xFFF8)
+    - protocolVersion: 2
+     - AsnIntegerHeader: 
+      - AsnId: Integer type (Universal 2)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00010) 2
+      - AsnLen: Length = 1, LengthOfLength = 0
+         Length: 1 bytes, LengthOfLength = 0
+       AsnInt: 2 (0x2)
+   - UserData: Identifier = Generic Conference Contro (0.0.20.124.0.1)
+    - UserDataHeader: 
+     - AsnId: OctetString type (Universal 4)
+      - LowTag: 
+         Class:    (00......) Universal (0)
+         Type:     (..0.....) Primitive
+         TagValue: (...00100) 4
+     - AsnLen: Length = 54, LengthOfLength = 0
+        Length: 54 bytes, LengthOfLength = 0
+    - AsnBerObjectIdentifier: Generic Conference Contro (0.0.20.124.0.1)
+     - AsnObjectIdentifierHeader: 
+      - AsnId: Reserved for use by the encoding rules (Universal 0)
+       - LowTag: 
+          Class:    (00......) Universal (0)
+          Type:     (..0.....) Primitive
+          TagValue: (...00000) 0
+      - AsnLen: Length = 5, LengthOfLength = 0
+         Length: 5 bytes, LengthOfLength = 0
+       First: 0 (0x0)
+       Final: 20 (0x14)
+       Final: 124 (0x7C)
+       Final: 0 (0x0)
+       Final: 1 (0x1)
+    - ConnectPDULength: 42
+       Align: No Padding
+       Length: 42
+    - ConnectGCCPDU: conferenceCreateResponse
+       ExtensionBit: 0 (0x0)
+     - ChoiceValue: conferenceCreateResponse
+        Value: (001.....) 0x1
+     - conferenceCreateResponse: 
+        ExtensionBit: 0 (0x0)
+        userDataPresent: 1 (0x1)
+      - nodeID: 0x79f3
+       - UserID: 31219
+        - Align: No Padding
+           Padding2: (00......) 0x0
+          Value: 30218 (0x760A)
+      - tag: 1 (0x1)
+       - Length: 1
+          Align: No Padding
+          Length: 1
+         Value: 1 (0x1)
+      - result: success
+         ExtensionBit: 0 (0x0)
+       - RootIndex: 0
+          Value: (000.....) 0x0
+      - userData: 
+       - Size: 1
+        - Align: No Padding
+           Padding4: (0000....) 0x0
+          Length: 1
+       - UserData: 0x4d63446e
+          valuePresent: 1 (0x1)
+        - key: h221NonStandard
+         - ChoiceValue: h221NonStandard
+            Value: (1.......) 0x1
+         - h221NonStandard: 
+          - H221NonStandardIdentifier: length: 4
+           - ConstrainedLength: 4
+              Value: (00000000) 0x0
+           - Align: No Padding
+              Padding6: (000000..) 0x0
+             Value: Binary Large Object (4 Bytes)
+        - ServerMcsConnectResponsePdu: 
+         - RDPGCCUserDataResponseLength: 32
+            Align: No Padding
+            Length: 32
+         - TsUd: SC_CORE
+          - TsUdHeader: Type = SC_CORE, Length = 12
+             Type: SC_CORE
+             Length: 12 (0xC)
+          - TsUdScCore: 
+             Version: RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 
+             ClientRequestedProtocols: TLS 1.0
+         - TsUd: SC_NET
+          - TsUdHeader: Type = SC_NET, Length = 8
+             Type: SC_NET
+             Length: 8 (0x8)
+          - TsUdScNet: 
+             MCSChannelID: 1003 (0x3EB)
+             ChannelCount: 0 (0x0)
+             Pad: 0 Bytes
+         - TsUd: SC_SECURITY
+          - TsUdHeader: Type = SC_SECURITY, Length = 12
+             Type: SC_SECURITY
+             Length: 12 (0xC)
+          - TsUdSCSec1: 
+           - EncryptionMethod: 
+              Support40Bit:  (...............................0) Not Support 
+              Support128Bit: (..............................0.) Not Support 128-bit
+              Reserved1:     (.............................0..)
+              Support56Bit:  (............................0...) Not Support 56-bit
+              SupportFIPS:   (...........................0....) Not Support FIPS Compliant
+              Reserved2:     (000000000000000000000000000.....)
+             EncryptionLevel: TS_ENCRYPTION_NONE
+ */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java
new file mode 100644
index 0000000..5862e19
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java
@@ -0,0 +1,149 @@
+// 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.Element;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class ServerMCSPDU extends BaseElement {
+
+  public ServerMCSPDU(String id) {
+    super(id);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    byte headerByte = buf.readSignedByte();
+    int type = headerByte >> 2;
+
+    switch (type) {
+    // Expected type: send data indication: 26 (0x1a, top 6 bits, or 0x68)
+    case 0x1a: {
+      // int userId = buf.readUnsignedShort() + 1001; // User ID: 1002 (1001+1)
+      buf.skipBytes(2); // Ignore user ID
+
+      int channelId = buf.readUnsignedShort(); // Channel ID: 1003
+
+      int flags = buf.readSignedByte();
+      if ((flags & 0x30) != 0x30)
+        throw new RuntimeException("Fragmented MCS packets are not supported.");
+
+      int payloadLength = buf.readVariableUnsignedShort();
+
+      ByteBuffer data = buf.readBytes(payloadLength);
+
+      buf.unref();
+
+      pushDataToPad("channel_" + channelId, data);
+      break;
+    }
+
+    case 0x8: {
+      // Disconnection sequence.
+      buf.unref();
+      break;
+    }
+
+    default:
+      throw new RuntimeException("Unsupported MCS packet type: " + type + "(" + headerByte + "), data: " + buf + ".");
+    }
+
+  }
+
+  /**
+   * 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[] {
+        // TPKT
+        (byte) 0x03, (byte) 0x00, // TPKT Header: TPKT version = 3
+        (byte) 0x00, (byte) 0x1B, // TPKT length: 27 bytes
+
+        // X224
+        (byte) 0x02, // X224 Length: 2 bytes
+        (byte) 0xF0, // X224 Type: Data
+        (byte) 0x80, // X224 EOT
+
+        // MCS
+        // Type: send data indication: 26 (0x1a, top 6 bits)
+        (byte) 0x68, // ??
+
+        (byte) 0x00, (byte) 0x01, // User ID: 1002 (1001+1)
+        (byte) 0x03, (byte) 0xEB, // Channel ID: 1003
+        (byte) 0x70, // Data priority: high, segmentation: begin|end
+        (byte) 0x0D, // Payload length: 13 bytes
+
+        // Deactivate all PDU
+        (byte) 0x0D, (byte) 0x00, // Length: 13 bytes (LE)
+
+        // - PDUType: 22 (0x16, LE)
+        // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU
+        // ProtocolVersion: (000000000001....) 1
+        (byte) 0x16, (byte) 0x00,
+
+        (byte) 0xEA, (byte) 0x03, // PDU source: 1002 (LE)
+        (byte) 0xEA, (byte) 0x03, (byte) 0x01, (byte) 0x00, // ShareID = 66538
+
+        (byte) 0x01, (byte) 0x00, // Length if source descriptor: 1 (LE)
+        (byte) 0x00, // Source descriptor (should be set to 0): 0
+    };
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element mcs = new ServerMCSPDU("mcs") {
+      {
+        verbose = true;
+      }
+    };
+    Element tpkt = new ServerTpkt("tpkt");
+    Element x224 = new ServerX224DataPdu("x224");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
+        // Deactivate all PDU
+        (byte) 0x0D, (byte) 0x00, // Length: 13 bytes (LE)
+
+        // - PDUType: 22 (0x16, LE)
+        // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU
+        // ProtocolVersion: (000000000001....) 1
+        (byte) 0x16, (byte) 0x00,
+
+        (byte) 0xEA, (byte) 0x03, // PDU source: 1002 (LE)
+        (byte) 0xEA, (byte) 0x03, (byte) 0x01, (byte) 0x00, // ShareID = 66538
+
+        (byte) 0x01, (byte) 0x00, // Length if source descriptor: 1 (LE)
+        (byte) 0x00, // Source descriptor (should be set to 0): 0
+    }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, tpkt, x224, mcs, sink);
+    pipeline.link("source", "tpkt", "x224", "mcs >channel_1003", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java
new file mode 100644
index 0000000..8c39a02
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java
@@ -0,0 +1,52 @@
+// 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;
+
+
+/**
+ * Try to determine packet content by it header fingerprint.
+ */
+public class ServerPacketSniffer extends PacketSniffer {
+
+  private static final Pair[] serverRegexps = new Pair[] {
+  // @formatter:off
+  new Pair("Server FastPath update",             "04"),
+  new Pair("Server X224ConnectionRequest",       "03 00 XX XX 0E D0"), 
+  new Pair("Server MCSConnectResponse",          "03 00 XX XX 02 F0 80 7F 66 5A"), 
+  new Pair("Server AttachUserConfirm",           "03 00 XX XX 02 F0 80 2E"),
+  new Pair("Server ChannelJoinConfirm",          "03 00 XX XX 02 F0 80 3E"),
+  new Pair("Server ErrorAlert",                  "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 14 80 00"),
+  new Pair("Server DemandActivePDU",             "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX XX 11"),
+  new Pair("Server ControlPDU",                  "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 14"),
+  new Pair("Server SynchronizePDU",              "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 1F"),
+  new Pair("Server FontMapPDU",                  "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 28"),
+  new Pair("Server SET_ERROR_INFO_PDU",          "03 00 XX XX 02 F0 80 68 00 01 03 EB 30 XX XX XX 17 00 00 00 EA 03 XX 00 XX XX XX XX 2F"),
+  new Pair("Server DeactivateAllPDU",            "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 16 00"),
+  new Pair("Server CloseConnection",             "03 00 00 09 02 F0 80 21 80"),
+  
+//  new Pair("Server TPKT unknown packet",         "03"),
+//  new Pair("Server FastPath update with flags or continuation",  ".*"),
+  // @formatter:on
+
+  };
+
+  public ServerPacketSniffer(String id) {
+    super(id, serverRegexps);
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java
new file mode 100644
index 0000000..3b0762e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java
@@ -0,0 +1,78 @@
+// 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.awt.image.IndexColorModel;
+
+import common.ScreenDescription;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240623.aspx
+ */
+public class ServerPaletteUpdate extends BaseElement {
+
+  public static final int UPDATETYPE_PALETTE = 0x0002;
+  protected ScreenDescription screen;
+
+  public ServerPaletteUpdate(String id, ScreenDescription screen) {
+    super(id);
+    this.screen = screen;
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    // (2 bytes): A 16-bit, unsigned integer. The update type. This field MUST
+    // be set to UPDATETYPE_PALETTE (0x0002).
+    int updateType = buf.readUnsignedShortLE();
+    if (updateType != UPDATETYPE_PALETTE)
+      throw new RuntimeException("Unexpected update type. Expected type: UPDATETYPE_PALETTE (0x0002), actual value: " + updateType + ", data: " + buf + ".");
+
+    // pad2Octets (2 bytes): A 16-bit, unsigned integer. Padding. Values in this
+    // field MUST be ignored.
+    buf.skipBytes(2);
+
+    // (4 bytes): A 32-bit, unsigned integer. The number of RGB triplets in the
+    // paletteData field. This field MUST be set to 256 (the number of entries
+    // in an 8 bpp palette).
+    int numberColors = (int) buf.readUnsignedIntLE();
+    if (numberColors != 256)
+      throw new RuntimeException("Unexpected value for number of color field in server Palette Update packet. Expected value: 256 colors, actual value: "
+          + numberColors + ", data: " + buf + ".");
+
+    // (variable): An array of palette entries in RGB triplet format packed on
+    // byte boundaries. The number of triplet entries is given by the
+    // numberColors field.
+    ByteBuffer paletteEntries = buf.readBytes(numberColors * 3);
+
+    // In the case of a Palette Update, the client MUST update the global
+    // palette on all drawing surfaces
+    screen.colorMap = new IndexColorModel(8, numberColors, paletteEntries.data, paletteEntries.offset, false);
+
+    /* DEBUG */buf.assertThatBufferIsFullyRead();
+
+    buf.unref();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java
new file mode 100644
index 0000000..315fbfe
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java
@@ -0,0 +1,115 @@
+// 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.Link;
+import streamer.OneTimeSwitch;
+
+public class ServerSynchronizePDU  extends OneTimeSwitch {
+
+  public ServerSynchronizePDU(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 + ".");
+    
+    // Ignore packet
+    buf.unref();
+    switchOff();
+  }
+
+}
+
+/* @formatter:off */
+/*
+
+ * 03 00 00 24 02 F0 80 68 00 01 03 EB 70 16 16 00 17 00 EA 03 EA 03 01 00 08 00 16 00 1F 00 00 00 01 00 86 A4 
+
+  Frame: Number = 36, Captured Frame Length = 93, MediaType = DecryptedPayloadHeader
++ DecryptedPayloadHeader: FrameCount = 1, ErrorStatus = SUCCESS
+  TLSSSLData: Transport Layer Security (TLS) Payload Data
++ TLS: TLS Rec Layer-1 SSL Application Data
+  ISOTS: TPKTCount = 1
+- TPKT: version: 3, Length: 36
+    version: 3 (0x3)
+    Reserved: 0 (0x0)
+    PacketLength: 36 (0x24)
+- X224: Data
+    Length: 2 (0x2)
+    Type: Data
+    EOT: 128 (0x80)
+- T125: Data Packet
+  - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003
+   - Type: Send Data Indication
+    - RootIndex: 26
+       Value: (011010..) 0x1a
+   - UserID: 0x3ea
+    - UserID: 0x3ea
+     - ChannelId: 1002
+      - Align: No Padding
+         Padding2: (00......) 0x0
+        Value: 1 (0x1)
+   - Channel: 0x3eb
+    - ChannelId: 1003
+       Align: No Padding
+       Value: 1003 (0x3EB)
+   - DataPriority: high
+    - DataPriority: high
+     - RootIndex: 1
+        Value: (01......) 0x1
+   - Segmentation: Begin End
+      Begin: (1.......) Begin
+      End:   (.1......) End
+   - Length: 22
+    - Align: No Padding
+       Padding4: (0000....) 0x0
+      Length: 22
+    RDP: RDPBCGR
+- RDPBCGR: SynchronizePDU
+  - SlowPathPacket: SynchronizePDU 
+   - SlowPath: Type = TS_PDUTYPE_DATAPDU
+    - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU
+       TotalLength: 22 (0x16)
+     - PDUType: 23 (0x17)
+        Type:            (............0111) TS_PDUTYPE_DATAPDU
+        ProtocolVersion: (000000000001....) 1
+       PDUSource: 1002 (0x3EA)
+    - SlowPathIoPacket: 0x0
+     - ShareDataHeader: TS_PDUTYPE2_SYNCHRONIZE
+        ShareID: 66538 (0x103EA)
+        Pad1: 8 (0x8)
+        StreamID: STREAM_UNDEFINED
+        UncompressedLength: 22 (0x16)
+        PDUType2: TS_PDUTYPE2_SYNCHRONIZE
+      - CompressedType: Not Compressed
+         MPPC:       (....0000) MPPC 8K
+         Reserved:   (...0....)
+         Compressed: (..0.....) Not Compressed
+         Front:      (.0......) Not At Front
+         Flush:      (0.......) Not Flushed
+        CompressedLength: 0 (0x0)
+     - TsSynchronizePDU: 0x1
+        MessageType: 0x1, MUST be set to SYNCMSGTYPE_SYNC (1)
+        TargetUser: 42118 (0xA486)
+ */

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java
new file mode 100644
index 0000000..0d19fa4
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.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;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class ServerTpkt extends BaseElement {
+
+  /**
+   * TPKT protocol version (first byte).
+   */
+  public static final int PROTOCOL_TPKT = 3;
+  
+  public ServerTpkt(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 + ".");
+
+    // We need at least 4 bytes to get packet length
+    if(!cap(buf, 4, UNLIMITED, link, false))
+      return;
+
+    int version = buf.readUnsignedByte();
+    if (version != PROTOCOL_TPKT)
+      throw new RuntimeException("Unexpected data in TPKT header. Expected TPKT version: 0x03,  actual value: " + buf + ".");
+
+    buf.skipBytes(1); // Reserved byte
+
+    // Length of whole packet, including header
+    int length = buf.readUnsignedShort();
+    if(!cap(buf, length, length, link, false))
+      return;
+
+    int payloadLength = length - buf.cursor;
+
+    // Extract payload
+    ByteBuffer outBuf = buf.slice(buf.cursor, payloadLength, true);
+    buf.unref();
+    
+    if(verbose) {
+      outBuf.putMetadata("source", this);
+    }
+
+    pushDataToAllOuts(outBuf);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/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
new file mode 100644
index 0000000..a335277
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.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;
+
+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/a98c473d/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
new file mode 100644
index 0000000..1548904
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java
@@ -0,0 +1,64 @@
+// 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/a98c473d/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
new file mode 100644
index 0000000..aaf93b0
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java
@@ -0,0 +1,40 @@
+// 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/a98c473d/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
new file mode 100644
index 0000000..d4c08f1
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java
@@ -0,0 +1,44 @@
+// 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/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java
new file mode 100644
index 0000000..1958475
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java
@@ -0,0 +1,107 @@
+// 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 streamer;
+
+import java.nio.charset.Charset;
+
+/**
+ * Assert that writes to this buffer are matching expected data.
+ */
+public class AssertingByteBuffer extends ByteBuffer {
+
+  public AssertingByteBuffer(byte[] expectedData) {
+    super(expectedData);
+  }
+
+  private void assertEquals(int expected, int actual) {
+    if (expected != actual)
+      throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + ", buf: "+this+".");
+  }
+
+  @Override
+  public void writeByte(int b) {
+    if(b<0)
+      throw new RuntimeException();
+    //*DEBUG*/System.out.println("WriteByte: "+b+", cursor:"+cursor+".");
+    assertEquals(readUnsignedByte(), b&0xff);
+  }
+
+  @Override
+  public void writeShort(int x) {
+    //*DEBUG*/System.out.println("WriteShort: "+x+", cursor:"+cursor+".");
+    assertEquals(readUnsignedShort(), x&0xFFff);
+  }
+
+  @Override
+  public void writeShortLE(int x) {
+    //*DEBUG*/System.out.println("WriteShortLE: "+x+", cursor:"+cursor+".");
+    assertEquals(readUnsignedShortLE(), x&0xFFff);
+  }
+
+  @Override
+  public void writeInt(int i) {
+    //*DEBUG*/System.out.println("WriteInt: "+i+", cursor:"+cursor+".");
+    assertEquals(readSignedInt(), i);
+  }
+
+  @Override
+  public void writeIntLE(int i) {
+    //*DEBUG*/System.out.println("WriteIntLE: "+i+", cursor:"+cursor+".");
+    assertEquals(readSignedIntLE(), i);
+  }
+
+  @Override
+  public void writeVariableIntLE(int i) {
+    //*DEBUG*/System.out.println("WriteVariableIntLE: "+i+", cursor:"+cursor+".");
+    assertEquals(readVariableSignedIntLE(), i);
+  }
+
+  @Override
+  public void writeString(String actual, Charset charset) {
+    //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+".");
+    String expected = readString(actual.length(), charset);
+    if (!actual.equals(expected))
+      throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + ".");
+  }
+
+  @Override
+  public void writeBytes(ByteBuffer actual) {
+    //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+".");
+    ByteBuffer expected = readBytes(actual.length);
+    if (!actual.equals(expected))
+      throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + ".");
+  }
+
+  @Override
+  public void writeBytes(byte[] actualData) {
+    ByteBuffer actual = new ByteBuffer(actualData);
+    //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+".");
+    ByteBuffer expected = readBytes(actual.length);
+    if (!actual.equals(expected))
+      throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + ".");
+  }
+
+  @Override
+  public void writeBytes(byte[] actualData, int offset, int length) {
+    ByteBuffer actual = new ByteBuffer(actualData, offset, length);
+    //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+".");
+    ByteBuffer expected = readBytes(actual.length);
+    if (!actual.equals(expected))
+      throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + ".");
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java
new file mode 100644
index 0000000..86f9be3
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java
@@ -0,0 +1,417 @@
+// 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 streamer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class BaseElement implements Element {
+
+  protected String id;
+
+  /**
+   * Constant for @see cap() method to indicate that length is not restricted.
+   */
+  public static final int UNLIMITED = -1;
+
+  /**
+   * Set to true to enable debugging messages.
+   */
+  protected boolean verbose = false;
+
+  /**
+   * Limit on number of packets sent to sink from this element. Can be handy for
+   * fake elements and handshake-related elements.
+   */
+  protected int numBuffers = 0;
+
+  /**
+   * Number of packets sent to sink.
+   */
+  protected int packetNumber = 0;
+
+  /**
+   * Recommended size for incoming buffer in pull mode.
+   */
+  protected int incommingBufLength = -1;
+
+  protected Map<String, DataSource> inputPads = new HashMap<String, DataSource>();
+  protected Map<String, DataSink> outputPads = new HashMap<String, DataSink>();
+
+  public BaseElement(String id) {
+    this.id = id;
+
+    verbose = System.getProperty("streamer.Element.debug", "false").equals("true") || System.getProperty("streamer.Element.debug", "").contains(id);
+  }
+
+  @Override
+  public String toString() {
+    return "Element(" + id + ")";
+  }
+
+  @Override
+  public Link getLink(String padName) {
+    if (inputPads.containsKey(padName))
+      return (Link) inputPads.get(padName);
+    else if (outputPads.containsKey(padName))
+      return (Link) outputPads.get(padName);
+    else
+      return null;
+  }
+
+  @Override
+  public Set<String> getPads(Direction direction) {
+    switch (direction) {
+    case IN:
+      return inputPads.keySet();
+
+    case OUT:
+      return outputPads.keySet();
+    }
+    return null;
+  }
+
+  @Override
+  public void validate() {
+    for (String padName : inputPads.keySet()) {
+      if (inputPads.get(padName) == null)
+        throw new RuntimeException("[ " + this + "] Required input pad is not connected. Pad name: " + padName + ".");
+    }
+
+    for (String padName : outputPads.keySet()) {
+      if (outputPads.get(padName) == null)
+        throw new RuntimeException("[ " + this + "] Required output pad is not connected. Pad name: " + padName + ".");
+    }
+  }
+
+  @Override
+  public void setLink(String padName, Link link, Direction direction) {
+    switch (direction) {
+    case IN:
+      if (inputPads.get(padName) != null)
+        throw new RuntimeException("Cannot link more than one wire to same pad. Element: " + this + ", pad: " + padName + ":" + direction + ", new link: "
+            + link + ", existing link: " + inputPads.get(padName) + ".");
+      inputPads.put(padName, link);
+      link.setSink(this);
+
+      break;
+
+    case OUT:
+      if (outputPads.get(padName) != null)
+        throw new RuntimeException("Cannot link more than one wire to same pad. Element: " + this + ", pad: " + padName + ":" + direction + ", new link: "
+            + link + ", existing link: " + outputPads.get(padName) + ".");
+
+      outputPads.put(padName, link);
+      link.setSource(this);
+
+      break;
+    }
+  }
+
+  @Override
+  public void dropLink(String padName) {
+    if (inputPads.containsKey(padName)) {
+      Link link = (Link) inputPads.remove(padName);
+      if (link != null)
+        link.setSink(null);
+    }
+
+    if (outputPads.containsKey(padName)) {
+      Link link = (Link) outputPads.remove(padName);
+      if (link != null)
+        link.setSource(null);
+    }
+  }
+
+  /**
+   * By default, try to pull data from input links.
+   * 
+   * Override this method in data source elements.
+   */
+  @Override
+  public void poll(boolean block) {
+    for (DataSource source : inputPads.values()) {
+      Link link = (Link) source;
+      ByteBuffer buf = link.pull(block);
+
+      if (buf != null) {
+        handleData(buf, link);
+      }
+    }
+  }
+
+  /**
+   * By default, do nothing with incoming data and retransmit data to all output
+   * pads.
+   */
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    pushDataToAllOuts(buf);
+  }
+
+  /**
+   * Send data to all output pads.
+   */
+  protected final void pushDataToAllOuts(ByteBuffer buf) {
+
+    if (buf == null)
+      return;
+
+    if (outputPads.size() == 0)
+      throw new RuntimeException("Number of outgoing connection is zero. Cannot send data to output. Data: " + buf + ".");
+
+    // Send data to all pads with OUT direction
+    for (DataSink out : outputPads.values()) {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Sending buf " + buf + " to " + out + ".");
+
+      buf.rewindCursor();
+      buf.ref();
+      out.sendData(buf);
+    }
+
+    buf.unref();
+    packetNumber++;
+  }
+
+  /**
+   * Send data to given pad only.
+   */
+  protected void pushDataToPad(String padName, ByteBuffer buf) {
+    if (buf == null)
+      return;
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Sending buf " + buf + " to " + padName + ".");
+
+    DataSink link = outputPads.get(padName);
+    if (link == null)
+      throw new RuntimeException("Output pad of " + this + " element is not connected to a link. Pad name: " + padName + ".");
+
+    buf.rewindCursor();
+    link.sendData(buf);
+  }
+
+  /**
+   * By default, do nothing with incoming event and retransmit event to all
+   * pads.
+   */
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Event " + event + ":" + direction + " is received.");
+
+    switch (event) {
+    case STREAM_CLOSE:
+      onClose();
+      break;
+    case STREAM_START:
+      onStart();
+      break;
+    }
+
+    sendEventToAllPads(event, direction);
+  }
+
+  /**
+   * Override this method to do something when STREAM_CLOSE event arrives.
+   */
+  protected void onClose() {
+  }
+
+  /**
+   * Override this method to do something when STREAM_START event arrives.
+   */
+  protected void onStart() {
+  }
+
+  /**
+   * Send event to all outputs.
+   * 
+   * @param event
+   *          a event
+   * @param direction
+   *          IN to send event to input pads, OUT to send event to all output
+   *          pads
+   */
+  protected final void sendEventToAllPads(Event event, Direction direction) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Sending event " + event + ":" + direction + ".");
+
+    switch (direction) {
+    case IN:
+      // Send event to all pads with IN direction
+      for (DataSource in : inputPads.values()) {
+        in.sendEvent(event, direction);
+      }
+      break;
+    case OUT:
+      // Send event to all pads with OUT direction
+      for (DataSink out : outputPads.values()) {
+        out.sendEvent(event, direction);
+      }
+      break;
+    }
+  }
+
+  /**
+   * Ensure that packet has required minimum and maximum length, cuts tail when
+   * necessary.
+   * 
+   * @param buf
+   *          a buffer
+   * @param minLength
+   *          minimum length of packet or -1
+   * @param maxLength
+   *          maximum length of packet or -1
+   * @param link
+   *          source link, to push unnecessary data back
+   * @param fromCursor
+   *          if true, then position will be included into calculation
+   * @return true,
+   */
+  public boolean cap(ByteBuffer buf, int minLength, int maxLength, Link link, boolean fromCursor) {
+
+    if (buf == null)
+      return false;
+
+    int length = buf.length;
+
+    int cursor;
+    if (fromCursor)
+      cursor = buf.cursor;
+    else
+      cursor = 0;
+
+    length -= cursor;
+
+    if ((minLength < 0 || length >= minLength) && (maxLength < 0 || length <= maxLength))
+      // Buffer is already in bounds
+      return true;
+
+    // Buffer is too small, wait for the rest of buffer
+    if (minLength >= 0 && length < minLength) {
+
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Buffer is too small. Min length: " + minLength + ", data length (after cursor): " + length + ".");
+
+      link.pushBack(buf.slice(0, length + cursor, true), minLength + cursor);
+      return false;
+    } else if (maxLength >= 0 && length > maxLength) {
+
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Buffer is too big. Max length: " + maxLength + ", data length (after cursor): " + length + ".");
+
+      // Buffer is too big, cut unnecessary tail
+      link.pushBack(buf.slice(maxLength + cursor, length - maxLength, true));
+      buf.length = maxLength + cursor;
+
+    }
+
+    return true;
+  }
+
+  @Override
+  public void dropLink(Link link) {
+    if (inputPads.containsValue(link)) {
+      for (Entry<String, DataSource> entry : inputPads.entrySet())
+        if (link.equals(entry.getValue())) {
+          inputPads.remove(entry.getKey());
+          break;
+        }
+    }
+
+    if (outputPads.containsValue(link)) {
+      for (Entry<String, DataSink> entry : outputPads.entrySet())
+        if (link.equals(entry.getValue())) {
+          outputPads.remove(entry.getKey());
+          break;
+        }
+    }
+  }
+
+  @Override
+  public void replaceLink(Link existingLink, Link newLink) {
+    if (inputPads.containsValue(existingLink)) {
+      for (Entry<String, DataSource> entry : inputPads.entrySet())
+        if (existingLink.equals(entry.getValue())) {
+          inputPads.put(entry.getKey(), newLink);
+          newLink.setSink(this);
+          break;
+        }
+    }
+
+    if (outputPads.containsValue(existingLink)) {
+      for (Entry<String, DataSink> entry : outputPads.entrySet())
+        if (existingLink.equals(entry.getValue())) {
+          outputPads.put(entry.getKey(), newLink);
+          newLink.setSource(this);
+          break;
+        }
+    }
+  }
+
+  @Override
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    Element source = new FakeSource("source") {
+      {
+        this.verbose = true;
+        this.numBuffers = 10;
+        this.incommingBufLength = 3;
+        this.delay = 100;
+      }
+    };
+
+    Element sink = new FakeSink("sink") {
+      {
+        this.verbose = true;
+      }
+    };
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source);
+    pipeline.add(new BaseElement("t1"));
+    pipeline.add(new BaseElement("t2"));
+    pipeline.add(new BaseElement("t3"));
+    pipeline.add(new BaseElement("t4"));
+    pipeline.add(sink);
+
+    pipeline.link("source", "t1", "t2", "t3", "t4", "sink");
+
+    // Links between source-t1-t2 will operate in pull mode.
+    // Links between t3-t4-sink will operate in push mode.
+    // Link between t2-t3 will run main loop (pull from source and push to
+    // sink).
+    pipeline.getLink("t3", STDOUT).run();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java
new file mode 100644
index 0000000..47f1435
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java
@@ -0,0 +1,36 @@
+// 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 streamer;
+
+public class BufferPool {
+  public static byte[] allocateNewBuffer(int minSize) {
+    // TODO: search for free buffer in pool
+    if (minSize >= 0)
+      return new byte[minSize];
+    else
+      // Return large buffer by default, too minimize number of round trips
+      // between to read full packet when packet is large, but it is important
+      // to return buffer to pool to reuse it (or null-ify links to it for
+      // faster GC)
+      // TODO: get free buffer from pool
+      return new byte[128 * 1024];
+  }
+
+  public static void recycleBuffer(byte[] buf) {
+    // TODO: return buffer to pool
+  }
+}


[8/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java
new file mode 100644
index 0000000..9a1caab
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java
@@ -0,0 +1,1132 @@
+// 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.Element;
+import streamer.Link;
+import streamer.MockSink;
+import streamer.MockSource;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+import common.ScreenDescription;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240488.aspx
+ */
+public class ClientConfirmActivePDU extends BaseElement {
+
+  public static final String SOURCE_DESC = "MSTSC";
+
+  public static final int CAPSTYPE_BITMAP = 0x2;
+
+  protected int numberCapabilities;
+  
+  protected RdpState state;
+  protected ScreenDescription screen;
+
+  protected boolean desktopResize = false;
+  protected int prefferedBitsPerPixel = 16;
+
+  public ClientConfirmActivePDU(String id, ScreenDescription screen, RdpState state) {
+    super(id);
+    this.state = state;
+    this.screen = screen;
+  }
+
+  @Override
+  public void handleData(ByteBuffer aBuf, Link link) {
+
+    // Body
+    ByteBuffer buf = new ByteBuffer(1024, true);
+    numberCapabilities = 0;
+    writeCapabilities(buf);
+    buf.trimAtCursor();
+
+    // Header
+    ByteBuffer header = createMCSHeader(buf);
+
+    // Length of source descriptor, including NULL character (LE)
+    header.writeShortLE(SOURCE_DESC.length() + 1);
+
+    // Length of combined capabilities + 4 bytes (number of capabilities and
+    // padding) (LE)
+    header.writeShortLE(buf.length + 4);
+
+    header.writeString(SOURCE_DESC, RdpConstants.CHARSET_8);
+    header.writeByte(0);
+
+    // Number of capabilities
+    header.writeShortLE(numberCapabilities);
+
+    // Padding 2 bytes
+    header.writeShortLE(0);
+
+    header.trimAtCursor();
+
+    // Prepend header to capabilities
+    buf.prepend(header);
+
+    // Trim buffer to actual length of data written
+    buf.length = buf.cursor;
+
+    pushDataToPad(STDOUT, buf);
+
+    sendOtherRequredPackets();
+
+  }
+
+  private ByteBuffer createMCSHeader(ByteBuffer buf) {
+    ByteBuffer header = new ByteBuffer(100);
+    // MCS Send Data Request
+    header.writeByte(0x64);
+
+    // Initiator: 1004 (1001+3)
+    header.writeShort(3);
+
+    // Channel ID: 1003 (I/O channel)
+    header.writeShort(1003);
+
+    // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+    header.writeByte(0x70);
+
+    int length = buf.length + 26;
+
+    // User data length: (variable length field, LE)
+    header.writeVariableShort(length);
+
+    // Total length: (LE)
+    header.writeShortLE(length);
+
+    // PDU type: Confirm Active PDU (0x3), TS_PROTOCOL_VERSION (0x10) (LE)
+    header.writeShortLE(0x13);
+
+    // PDU source: 1004 (LE)
+    header.writeShortLE(1004);
+
+    // Share ID, e.g. 0x000103ea (LE)
+    header.writeIntLE((int) state.serverShareId);
+
+    // Originator ID: 1002 (LE)
+    header.writeShortLE(1002);
+    return header;
+  }
+
+  private void sendOtherRequredPackets() {
+    // Send sequence in bulk
+
+    sendSynchronizePDU();
+    sendControlPDUActionCooperate();
+    sendControlPDUActionRequestControl();
+    // sendBitmapCachePersistentListPDU();
+    sendFontListPDU();
+  }
+
+  private void sendFontListPDU() {
+    {
+      int length = 1024; // Large enough
+      ByteBuffer buf = new ByteBuffer(length, true);
+
+      /* @formatter:off */
+      buf.writeBytes(new byte[] {
+          // MCS Send Data Request
+          (byte)0x64,
+          // Initiator: 1004 (1001+3)
+          (byte)0x00, (byte)0x03, 
+          // Channel ID: 1003 (I/O channel)
+          (byte)0x03, (byte)0xeb, 
+          // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+          (byte)0x70, 
+          // User data length: 26 bytes (0x1a, variable length field)
+          (byte)0x80, (byte)0x1a,
+          
+          // Total length: 26 bytes (0x1a, LE)
+          (byte)0x1a, (byte)0x00, 
+          // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE)
+          (byte)0x17, (byte)0x00, 
+          // PDU source: 1004 (LE)
+          (byte)0xec, (byte)0x03, 
+      });
+      // Share ID, 4 bytes  (LE)
+      buf.writeIntLE((int)state.serverShareId);
+      
+      buf.writeBytes(new byte[] {
+          // Padding 1 byte
+          (byte)0x00, 
+          // Stream ID: STREAM_LOW (1)
+          (byte)0x01, 
+          // uncompressedLength : 12 bytes (LE)
+          (byte)0x0c, (byte)0x00,
+          
+          // pduType2: PDUTYPE2_FONTLIST (39)
+          (byte)0x27, 
+          // generalCompressedType: 0
+          (byte)0x00, 
+          // generalCompressedLength: 0 (LE)
+          (byte)0x00, (byte)0x00, 
+
+          // numberEntries (should be set to zero): 0 (LE)
+          (byte)0x00, (byte)0x00,
+          // totalNumEntries (should be set to zero): 0 (LE)
+          (byte)0x00, (byte)0x00,
+          // listFlags  (should be set to 0x3): 0x0003 (LE), FONTLIST_LAST(0x2) | FONTLIST_FIRST(0x1) 
+          (byte)0x03, (byte)0x00,
+          // entrySize: 50 bytes (0x0032, LE)
+          (byte)0x32, (byte)0x00,
+      });
+      /* @formatter:on */
+
+      // Trim buffer to actual length of data written
+      buf.trimAtCursor();
+
+      pushDataToPad(STDOUT, buf);
+    }
+  }
+
+  private void sendControlPDUActionRequestControl() {
+    int length = 1024; // Large enough
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    /* @formatter:off */
+    buf.writeBytes(new byte[] {
+        // MCS Send Data Request
+        (byte)0x64, 
+        // Initiator: 1004 (1001+3)
+        (byte)0x00, (byte)0x03,
+        // Channel ID: 1003 (I/O channel)
+        (byte)0x03, (byte)0xeb, 
+        // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+        (byte)0x70, 
+        // User data length: 26 bytes (0x1a, variable length field)
+        (byte)0x80, (byte)0x1a,
+        
+        // Total length: 26 bytes (0x1a, LE)
+        (byte)0x1a, (byte)0x00,
+        // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE)
+        (byte)0x17, (byte)0x00, 
+        // PDU source: 1004 (LE)
+        (byte)0xec, (byte)0x03,
+    });
+        // Share ID, 4 bytes  (LE)
+    buf.writeIntLE((int)state.serverShareId);
+        
+    buf.writeBytes(new byte[] {
+        // Padding 1 byte
+        (byte)0x00, 
+        // Stream ID: STREAM_LOW (1)
+        (byte)0x01, 
+        // uncompressedLength : 12 bytes (LE)
+        (byte)0x0c, (byte)0x00, 
+        // pduType2: PDUTYPE2_CONTROL (20)
+        (byte)0x14, 
+        // generalCompressedType: 0
+        (byte)0x00, 
+        // generalCompressedLength: 0 (LE)
+        (byte)0x00, (byte)0x00,
+        
+        // action: CTRLACTION_REQUEST_CONTROL (1) (LE)
+        (byte)0x01, (byte)0x00,
+        // grantId: 0 (LE)
+        (byte)0x00, (byte)0x00, 
+        // controlId: 0 (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+    });
+    /* @formatter:on */
+
+    // Trim buffer to actual length of data written
+    buf.trimAtCursor();
+
+    pushDataToPad(STDOUT, buf);
+  }
+
+  private void sendControlPDUActionCooperate() {
+    int length = 1024; // Large enough
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    /* @formatter:off */
+    buf.writeBytes(new byte[] {
+        // MCS Send Data Request
+        (byte)0x64, 
+        // Initiator: 1004 (1001+3)
+        (byte)0x00, (byte)0x03, 
+        // Channel ID: 1003 (I/O channel)
+        (byte)0x03, (byte)0xeb, 
+        // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+        (byte)0x70, 
+        // User data length: 26 bytes (0x1a, variable length field)
+        (byte)0x80, (byte)0x1a,
+        
+        // Total length: 26 bytes (0x1a, LE)
+        (byte)0x1a,(byte)0x00,
+        // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE)
+        (byte)0x17, (byte)0x00, 
+        // PDU source: 1004 (LE)
+        (byte)0xec, (byte)0x03, 
+    });
+    // Share ID, 4 bytes  (LE)
+    buf.writeIntLE((int)state.serverShareId);
+    
+    buf.writeBytes(new byte[] {
+        // Padding 1 byte
+        (byte)0x00, 
+        // Stream ID: STREAM_LOW (1)
+        (byte)0x01, 
+        // uncompressedLength : 12 bytes (LE)
+        (byte)0x0c, (byte)0x00, 
+        // pduType2: PDUTYPE2_CONTROL (20)
+        (byte)0x14, 
+        // generalCompressedType: 0
+        (byte)0x00, 
+        // generalCompressedLength: 0 (LE?)
+        (byte)0x00, (byte)0x00, 
+        // action: CTRLACTION_COOPERATE (4) (LE)
+        (byte)0x04, (byte)0x00,
+        // grantId: 0 (LE)
+        (byte)0x00, (byte)0x00,
+        // controlId: 0
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+    });
+    /* @formatter:on */
+
+    buf.trimAtCursor();
+
+    pushDataToPad(STDOUT, buf);
+  }
+
+  private void sendSynchronizePDU() {
+
+    ByteBuffer buf = new ByteBuffer(1024, true);
+    /* @formatter:off */
+    buf.writeBytes(new byte[] {
+        // MCS send data request
+        (byte)0x64,
+        // Initiator: 1004 (1001+3)
+        (byte)0x00, (byte)0x03,
+        // Channel ID: 1003 (I/O Channel)
+        (byte)0x03, (byte)0xeb,
+        // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+        (byte)0x70, 
+        // Data length:  22 bytes (0x16, variable length field)
+        (byte)0x80,  (byte)0x16, 
+        
+        // RDP: total length: 22 bytes (LE)
+        (byte)0x16, (byte)0x00, 
+        
+        // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE)
+        (byte)0x17, (byte)0x00,
+        
+        // PDU source: 1007 (LE)
+        (byte)0xec, (byte)0x03,
+    });
+    // Share ID, 4 bytes  (LE)
+    buf.writeIntLE((int)state.serverShareId);
+    
+    buf.writeBytes(new byte[] {
+        // Padding: 1 byte
+        (byte)0x00,
+        // Stream ID: STREAM_LOW (1)
+        (byte)0x01, 
+        // uncompressedLength : 8 bytes (LE)
+        (byte)0x08, (byte)0x00,
+        // pduType2 = PDUTYPE2_SYNCHRONIZE (31)
+        (byte)0x1f, 
+        // generalCompressedType: 0
+        (byte)0x00,
+        // generalCompressedLength: 0 (LE?)
+        (byte)0x00, (byte)0x00,
+        //  messageType: SYNCMSGTYPE_SYNC (1) (LE)
+        (byte)0x01, (byte)0x00, 
+        // targetUser: 0x03ea
+        (byte)0xea, (byte)0x03,
+    });
+    /* @formatter:on */
+    buf.trimAtCursor();
+    pushDataToPad(STDOUT, buf);
+  }
+
+  private void writeCapabilities(ByteBuffer buf) {
+    writeGeneralCS(buf);
+
+    writeBitmapCS(buf);
+
+    writeOrderCS(buf);
+
+    writeBitmapCache2CS(buf);
+
+    writeColorTableCacheCS(buf);
+
+    writeWindowActivationCS(buf);
+
+    writeControlCS(buf);
+
+    writePointerCS(buf);
+
+    writeShareCS(buf);
+
+    writeInputCS(buf);
+
+    writeBrushCS(buf);
+
+    writeSoundCS(buf);
+
+    writeFontCS(buf);
+
+    writeOffscreenBitmapCS(buf);
+
+    writeGlyphCacheCS(buf);
+  }
+
+  private void writeBrushCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Brush Capability Set (8 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240564.aspx
+        (byte) 0x0f, (byte) 0x00, // capability set type: CAPSTYPE_BRUSH (15,
+                                  // LE)
+        (byte) 0x08, (byte) 0x00, // length of capability set: 8 bytes (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // brushSupportLevel:
+                                                            // BRUSH_DEFAULT
+                                                            // (0x0, LE)
+
+    });
+  }
+
+  private void writeInputCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Input Capability Set (88 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240563.aspx
+        (byte) 0x0d,
+        (byte) 0x00, // capability set type: CAPSTYPE_INPUT (13, LE)
+        (byte) 0x58,
+        (byte) 0x00, // length of capability set: 88 bytes (LE)
+        (byte) 0x35,
+        (byte) 0x00, // inputFlags: 0x0035 (LE), INPUT_FLAG_FASTPATH_INPUT2
+                     // (0x20), INPUT_FLAG_VKPACKET (0x10), INPUT_FLAG_MOUSEX
+                     // (0x4), INPUT_FLAG_SCANCODES (0x1)
+        (byte) 0x00,
+        (byte) 0x00, // Padding 2 bytes
+        (byte) 0x09,
+        (byte) 0x04,
+        (byte) 0x00,
+        (byte) 0x00, // keyboardLayout: "US" keyboard layout (0x000409, LE)
+        (byte) 0x00,
+        (byte) 0x00,
+        (byte) 0x00,
+        (byte) 0x00, // keyboardType: unknown (LE)
+        (byte) 0x00,
+        (byte) 0x00,
+        (byte) 0x00,
+        (byte) 0x00, // keyboardSubType: unknown (LE)
+        (byte) 0x00,
+        (byte) 0x00,
+        (byte) 0x00,
+        (byte) 0x00, // keyboardFunctionKey: unknown (LE)
+        // imeFileName: "", (64 bytes, including trailing NULL characters, UCS2)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+    });
+  }
+
+  private void writeShareCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Share Capability Set (8 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240570.aspx
+        (byte) 0x09, (byte) 0x00, // capability set type: CAPSTYPE_SHARE (9, LE)
+        (byte) 0x08, (byte) 0x00, // length of capability set: 8 bytes (LE)
+        (byte) 0x00, (byte) 0x00, // nodeID (must be set to 0 by client): 0 (LE)
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes (LE)
+
+    });
+  }
+
+  private void writePointerCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Pointer Capability Set (10 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240562.aspx
+        (byte) 0x08, (byte) 0x00, // capability set type: CAPSTYPE_POINTER (8,
+                                  // LE)
+        (byte) 0x0a, (byte) 0x00, // length of capability set: 10 bytes (LE)
+        (byte) 0x00, (byte) 0x00, // colorPointerFlag: FALSE (LE)
+        (byte) 0x00, (byte) 0x00, // colorPointerCacheSize: 0 (LE)
+        (byte) 0x14, (byte) 0x00, // pointerCacheSize: 20 (LE)
+
+    });
+  }
+
+  private void writeControlCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Control Capability Set (12 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240568.aspx
+        (byte) 0x05, (byte) 0x00, // capability set type: CAPSTYPE_ACTIVATION
+                                  // (7)
+        (byte) 0x0c, (byte) 0x00, // length of capability set: 12 bytes (LE)
+        (byte) 0x00, (byte) 0x00, // controlFlags (should be set to 0): 0 (LE)
+        (byte) 0x00, (byte) 0x00, // remoteDetachFlag (should be set to 0): 0
+                                  // (LE)
+        (byte) 0x02, (byte) 0x00, // controlInterest (should be set to
+                                  // CONTROLPRIORITY_NEVER):
+                                  // CONTROLPRIORITY_NEVER (2) (LE)
+        (byte) 0x02, (byte) 0x00, // detachInterest (should be set to
+                                  // CONTROLPRIORITY_NEVER):
+                                  // CONTROLPRIORITY_NEVER (2) (LE)
+
+    });
+  }
+
+  private void writeWindowActivationCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Window Activation Capability Set (12 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240569.aspx
+        (byte) 0x07, (byte) 0x00, // capability set type: CAPSTYPE_ACTIVATION
+                                  // (7) (LE)
+        (byte) 0x0c, (byte) 0x00, // length of capability set: 12 bytes (LE)
+        (byte) 0x00, (byte) 0x00, // helpKeyFlag (should be set to FALSE (0)):
+                                  // FALSE (0, LE)
+        (byte) 0x00, (byte) 0x00, // helpKeyIndexFlag (should be set to FALSE
+                                  // (0)): FALSE (0, LE)
+        (byte) 0x00, (byte) 0x00, // helpExtendedKeyFlag (should be set to FALSE
+                                  // (0)): FALSE (0, LE)
+        (byte) 0x00, (byte) 0x00, // windowManagerKeyFlag (should be set to
+                                  // FALSE (0)): FALSE (0, LE)
+
+    });
+  }
+
+  private void writeColorTableCacheCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+
+        //
+        // Color Table Cache Capability Set (8 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc241564.aspx
+        (byte) 0x0a, (byte) 0x00, // capability set type: CAPSTYPE_COLORCACHE
+                                  // (10) (LE)
+        (byte) 0x08, (byte) 0x00, // length of capability set: 8 bytes (LE)
+        (byte) 0x06, (byte) 0x00, // Color table cache size (must be ignored
+                                  // during capability exchange and is assumed
+                                  // to be 0x0006): 6 (LE)
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+
+    });
+  }
+
+  private void writeBitmapCache2CS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Bitmap Cache Rev. 2 Capability Set (40 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240560.aspx
+        (byte) 0x13, (byte) 0x00, // capability set type:
+                                  // CAPSTYPE_BITMAPCACHE_REV2 (19) (LE)
+        (byte) 0x28, (byte) 0x00, // length of capability set: 40 bytes (LE)
+        (byte) 0x00, (byte) 0x00, // Cache flags: 0 (LE)
+        (byte) 0x00, // Padding 1 byte
+        (byte) 0x00, // Number of cell caches: 0
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Bitmap cache0
+                                                            // cell info: 0 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Bitmap cache1
+                                                            // cell info: 0 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Bitmap cache2
+                                                            // cell info: 0 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Bitmap cache3
+                                                            // cell info: 0 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Bitmap cache4
+                                                            // cell info: 0 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Padding 12 bytes
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Padding
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Padding
+    });
+  }
+
+  private void writeGeneralCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        // Capabilities, see
+        // http://msdn.microsoft.com/en-us/library/cc240486.aspx
+
+        //
+        // General capability set (24 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240549.aspx
+        (byte) 0x01, (byte) 0x00, // capability set type: CAPSTYPE_GENERAL (1)
+                                  // (LE)
+        (byte) 0x18, (byte) 0x00, // length of capability set: 24 bytes (LE)
+        (byte) 0x01, (byte) 0x00, // TS_OSMAJORTYPE_WINDOWS (1) (LE)
+        (byte) 0x03, (byte) 0x00, // TS_OSMINORTYPE_WINDOWS_NT (3) (LE)
+        (byte) 0x00, (byte) 0x02, // TS_CAPS_PROTOCOLVERSION (0x0200) (LE)
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+        (byte) 0x00, (byte) 0x00, // generalCompressionTypes: 0 (LE)
+
+        // Extra flags: 0x040d (LE)
+        // FastPathOutput: (...............1) Advertiser supports fast-path
+        // output
+        // ShadowCompression: (..............0.) Advertiser NOT supports shadow
+        // compression
+        // LongLengthCredentials: (.............1..) Advertiser supports
+        // long-length credentials for the user name, password, or domain name
+        // SessionAutoreconnection: (............1...) Advertiser supports
+        // session auto-reconnection
+        // ImprovedEncryptionChecksum: (...........0....) Client and server NOT
+        // support improved encryption checksum
+        // Reserved1: (......00000.....)
+        // CompressedBitMapDataFlag: (.....1..........) No 8-UINT8 header is
+        // present for compressed bitmap data
+        // Reserved2: (00000...........)
+        (byte) 0x0d, (byte) 0x04,
+
+        (byte) 0x00, (byte) 0x00, // updateCapabilityFlag: 0 (LE)
+        (byte) 0x00, (byte) 0x00, // remoteUnshareFlag: 0 (LE)
+        (byte) 0x00, (byte) 0x00, // generalCompressionLevel: 0 (LE)
+        (byte) 0x00, // refreshRectSupport: FALSE (0)
+        (byte) 0x00, // suppressOutputSupport: FALSE (0)
+
+    });
+  }
+
+  private void writeBitmapCS(ByteBuffer buf) {
+    // Bitmap capability set (28 bytes), see
+    // http://msdn.microsoft.com/en-us/library/cc240554.aspx
+    
+    numberCapabilities++;
+    
+    // Capability set type: CAPSTYPE_BITMAP (2) (LE)
+    buf.writeShortLE(CAPSTYPE_BITMAP);
+    
+    // Length of capability set: 28 bytes (LE)
+    buf.writeShortLE(28);
+    
+    // preferredBitsPerPixel: 16 bpp (LE)
+    buf.writeShortLE(prefferedBitsPerPixel);
+    
+    // receive1BitPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE)
+    buf.writeShortLE(1);
+    
+    // receive4BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE)
+    buf.writeShortLE(1);
+    
+    // receive8BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE)
+    buf.writeShortLE(1);
+    
+    // Desktop width and height (LE)
+    buf.writeShortLE(screen.getFramebufferWidth());
+    buf.writeShortLE(screen.getFramebufferHeight());
+    
+    // Padding 2 bytes
+    buf.writeShortLE(0);
+    
+   // desktopResizeFlag (LE)
+    buf.writeShortLE((desktopResize )?1:0);
+    
+    buf.writeBytes(new byte[] {
+        (byte) 0x01, (byte) 0x00, // bitmapCompressionFlag (must be set to TRUE
+                                  // (0x1)): TRUE (0x1) (LE)
+        (byte) 0x00, // highColorFlags (field is ignored and SHOULD be set to
+                     // zero): 0
+        (byte) 0x01, // drawingFlags: 0x1 TODO: padding, why 0x1 ???
+        (byte) 0x01, (byte) 0x00, // multipleRectangleSupport: TRUE (LE)
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+
+    });
+  }
+
+  private void writeOrderCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Order Capability Set (88 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240556.aspx
+        (byte) 0x03,
+        (byte) 0x00, // capability set type: CAPSTYPE_ORDER (3) (LE)
+        (byte) 0x58,
+        (byte) 0x00, // length of capability set: 88 bytes (LE)
+        // terminalDescriptor = "" (16 bytes, UCS2)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // pad4octetsA
+        (byte) 0x01, (byte) 0x00, // desktopSaveXGranularity (ignored): 1 (LE)
+        (byte) 0x14, (byte) 0x00, // desktopSaveYGranularity (ignored): 20 (LE)
+        (byte) 0x00, (byte) 0x00, // pad2octetsA (ignored)
+        (byte) 0x01, (byte) 0x00, // maximumOrderLevel: ORD_LEVEL_1_ORDERS (1)
+        (byte) 0x00, (byte) 0x00, // number of fonts (ignored): 0
+        (byte) 0x4a, (byte) 0x00, // orderFlags = 0x004a (LE),
+                                  // SOLIDPATTERNBRUSHONLY (0x40),
+                                  // ZEROBOUNDSDELTASSUPPORT (0x8, MUST),
+                                  // NEGOTIATEORDERSUPPORT (0x2, MUST)
+        // Order support: 32 bytes (no primary drawing orders are supported, so
+        // this array MUST be initialized to all zeros, use 0x01 for TRUE).
+        (byte) 0x00, // TS_NEG_DSTBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_PATBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_SCRBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MEMBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MEM3BLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_ATEXTOUT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_AEXTTEXTOUT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_DRAWNINEGRID_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_LINETO_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MULTI_DRAWNINEGRID_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_OPAQUERECT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_SAVEBITMAP_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_WTEXTOUT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MEMBLT_R2_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MEM3BLT_R2_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MULTIDSTBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MULTIPATBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MULTISCRBLT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_MULTIOPAQUERECT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_FAST_INDEX_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_POLYGON_SC_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_POLYGON_CB_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_POLYLINE_INDEX: TRUE
+        (byte) 0x00, // Unused: 0
+        (byte) 0x00, // TS_NEG_FAST_GLYPH_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_ELLIPSE_SC_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_ELLIPSE_CB_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_INDEX_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_WEXTTEXTOUT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_WLONGTEXTOUT_INDEX: FALSE
+        (byte) 0x00, // TS_NEG_WLONGEXTTEXTOUT_INDEX: FALSE
+        (byte) 0x00, // Unused: 0
+        (byte) 0x00, (byte) 0x00, // Text flags (ignored): 0 (LE)
+        (byte) 0x00, (byte) 0x00, // Order support extra flags: 0 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Padding 4 bytes
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Desktop save size
+                                                            // (ignored): 0
+                                                            // (assumed to be
+                                                            // 230400 bytes
+                                                            // (480*480,
+                                                            // 0x38400, LE))
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+        (byte) 0xe4, (byte) 0x04, // Text ANSI Code Page: 1252, ANSI - Latin I
+                                  // (0x04e4, LE)
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+
+    });
+  }
+
+  private void writeSoundCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Sound Capability Set (8 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240552.aspx
+        (byte) 0x0c, (byte) 0x00, // capability set type: CAPSTYPE_SOUND (12,
+                                  // LE)
+        (byte) 0x08, (byte) 0x00, // length of capability set: 8 bytes (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // soundFlags:
+                                                            // 0x0000 (LE) //
+                                                            // SOUND_FLAG_BEEPS
+                                                            // (0x1)
+
+    });
+  }
+
+  private void writeFontCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Font Capability Set (8 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240571.aspx
+        (byte) 0x0e, (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+    });
+  }
+
+  private void writeOffscreenBitmapCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Offscreen Bitmap Cache Capability Set (12 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240550.aspx
+        (byte) 0x11, (byte) 0x00, // capability set type:
+                                  // CAPSTYPE_OFFSCREENCACHE (17, LE)
+        (byte) 0x0c, (byte) 0x00, // length of capability set: 12 bytes (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // offscreenSupportLevel:
+                                                            // FALSE (LE)
+        (byte) 0x00, (byte) 0x00, // offscreenCacheSize: 0 (LE)
+        (byte) 0x00, (byte) 0x00, // offscreenCacheEntries: 0 (LE)
+
+    });
+  }
+
+  private void writeGlyphCacheCS(ByteBuffer buf) {
+    numberCapabilities++;
+    buf.writeBytes(new byte[] {
+        //
+        // Glyph Cache Capability Set (52 bytes), see
+        // http://msdn.microsoft.com/en-us/library/cc240565.aspx
+        (byte) 0x10, (byte) 0x00, // capability set type:
+                                  // CAPSTYPE_OFFSCREENCACHE (16, LE)
+        (byte) 0x34, (byte) 0x00, // length of capability set: 52 bytes (LE)
+        // Glyph Cache (40 bytes)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x04, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x04, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x08, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x08, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x10, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x20, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x40, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x80, (byte) 0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0xfe, (byte) 0x00, // CacheEntries: 254 (LE)
+        (byte) 0x00, (byte) 0x01, // CacheMaximumCellSize: 4 (LE)
+        (byte) 0x40, (byte) 0x00, // CacheEntries: 64 (LE)
+        (byte) 0x00, (byte) 0x08, // CacheMaximumCellSize: 2048 (LE)
+        // FragCache
+        (byte) 0x00, (byte) 0x01, // CacheEntries: 256 (LE)
+        (byte) 0x00, (byte) 0x01, // CacheMaximumCellSize: 256 (LE)
+        //
+        (byte) 0x00, (byte) 0x00, // GlyphSupportLevel: GLYPH_SUPPORT_NONE (0x0,
+                                  // LE)
+        (byte) 0x00, (byte) 0x00, // Padding 2 bytes
+    });
+  }
+
+  /**
+   * 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[] {
+        // MCS Send Data Request
+        (byte)0x64,  
+        
+        // Initiator: 1004 (1001+3)
+        (byte)0x00, (byte)0x03, 
+        
+        // Channel ID: 1003 (I/O channel)
+        (byte)0x03, (byte)0xeb,
+        
+        // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10)
+        (byte)0x70,
+        
+        // User data length: 432 bytes (0x1b0, variable length field)
+        (byte)0x81, (byte)0xb0, 
+        
+        // Total length: 432 bytes (0x1b0, LE)
+        (byte)0xb0, (byte)0x01, 
+        
+        // PDU type: Confirm Active PDU (0x3), TS_PROTOCOL_VERSION (0x10) (LE)
+        (byte)0x13, (byte)0x00, 
+        
+        // PDU source: 1004 (LE)
+        (byte)0xec, (byte)0x03, 
+        
+        // Share ID: 0x000103ea (LE)
+        (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00,
+        
+        // Originator ID: 1002 (LE)
+        (byte)0xea, (byte)0x03,
+        
+        // Length of source descriptor: 6 bytes (including NULL character) (LE) 
+        (byte)0x06, (byte)0x00, 
+        
+        // Length of combined capabilities: 410 bytes (LE)
+        (byte)0x9a, (byte)0x01,
+        
+        // Source descriptor: "MSTSC" ???
+        (byte)0x4d, (byte)0x53, (byte)0x54, (byte)0x53, (byte)0x43, (byte)0x00,
+        
+        // Number of capabilities: 15 (LE)
+        (byte)0x0f, (byte)0x00,
+        
+        // Padding 2 bytes
+        (byte)0x00, (byte)0x00,
+        
+        // Capabilities, see http://msdn.microsoft.com/en-us/library/cc240486.aspx
+        
+        //
+        // General capability set (24 bytes), see http://msdn.microsoft.com/en-us/library/cc240549.aspx
+        (byte)0x01, (byte)0x00, // capability set type: CAPSTYPE_GENERAL (1) (LE) 
+        (byte)0x18, (byte)0x00, // length of capability set: 24 bytes (LE)
+        (byte)0x01, (byte)0x00, // TS_OSMAJORTYPE_WINDOWS (1) (LE)
+        (byte)0x03, (byte)0x00, // TS_OSMINORTYPE_WINDOWS_NT (3) (LE)
+        (byte)0x00, (byte)0x02, // TS_CAPS_PROTOCOLVERSION (0x0200) (LE)
+        (byte)0x00, (byte)0x00, // Padding 2 bytes
+        (byte)0x00, (byte)0x00, // generalCompressionTypes: 0 (LE)
+        
+        // Extra flags: 0x040d (LE)
+//        FastPathOutput:             (...............1) Advertiser supports fast-path output
+//        ShadowCompression:          (..............0.) Advertiser NOT supports shadow compression
+//        LongLengthCredentials:      (.............1..) Advertiser supports long-length credentials for the user name, password, or domain name
+//        SessionAutoreconnection:    (............1...) Advertiser supports session auto-reconnection
+//        ImprovedEncryptionChecksum: (...........0....) Client and server NOT support improved encryption checksum
+//        Reserved1:                  (......00000.....)
+//        CompressedBitMapDataFlag:   (.....1..........) No 8-UINT8 header is present for compressed bitmap data
+//        Reserved2:                  (00000...........)
+        (byte)0x0d, (byte)0x04,
+        
+        (byte)0x00, (byte)0x00, // updateCapabilityFlag: 0 (LE)
+        (byte)0x00, (byte)0x00, // remoteUnshareFlag: 0 (LE)
+        (byte)0x00, (byte)0x00, // generalCompressionLevel: 0 (LE)
+        (byte)0x00, // refreshRectSupport: FALSE (0)
+        (byte)0x00, // suppressOutputSupport: FALSE (0)
+        
+        //
+        // Bitmap capability set (28 bytes), see http://msdn.microsoft.com/en-us/library/cc240554.aspx
+        (byte)0x02, (byte)0x00, // capability set type: CAPSTYPE_BITMAP (2) (LE)
+        (byte)0x1c, (byte)0x00, // length of capability set: 28 bytes (LE)
+        (byte)0x10, (byte)0x00, // preferredBitsPerPixel: 16 bpp (LE)
+        (byte)0x01, (byte)0x00, // receive1BitPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE)
+        (byte)0x01, (byte)0x00, // receive4BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE)
+        (byte)0x01, (byte)0x00, // receive8BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE)
+        (byte)0x00, (byte)0x04, // desktopWidth = 1024 pixels (LE)
+        (byte)0x00, (byte)0x03, // desktopHeight = 768 pixels (LE)
+        (byte)0x00, (byte)0x00, // Padding 2 bytes
+        (byte)0x00, (byte)0x00, // desktopResizeFlag: FALSE (0x0) (LE)
+        (byte)0x01, (byte)0x00, // bitmapCompressionFlag (must be set to TRUE (0x1)): TRUE (0x1) (LE)
+        (byte)0x00, // highColorFlags (field is ignored and SHOULD be set to zero): 0 
+        (byte)0x01, // drawingFlags: 0x1 TODO: padding, why 0x1 ???
+        (byte)0x01, (byte)0x00, // multipleRectangleSupport: TRUE (LE) 
+        (byte)0x00, (byte)0x00, // Padding 2 bytes
+        
+        //
+        // Order Capability Set (88 bytes), see http://msdn.microsoft.com/en-us/library/cc240556.aspx
+        (byte)0x03, (byte)0x00, // capability set type: CAPSTYPE_ORDER (3) (LE)
+        (byte)0x58, (byte)0x00, // length of capability set: 88 bytes (LE)
+        // terminalDescriptor = "" (16 bytes, UCS2)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // pad4octetsA 
+        (byte)0x01, (byte)0x00, // desktopSaveXGranularity (ignored): 1 (LE)
+        (byte)0x14, (byte)0x00, // desktopSaveYGranularity (ignored): 20 (LE)
+        (byte)0x00, (byte)0x00, // pad2octetsA (ignored)
+        (byte)0x01, (byte)0x00, // maximumOrderLevel: ORD_LEVEL_1_ORDERS (1) 
+        (byte)0x00, (byte)0x00, // number of fonts (ignored): 0 
+        (byte)0x4a, (byte)0x00, // orderFlags = 0x004a (LE), SOLIDPATTERNBRUSHONLY (0x40), ZEROBOUNDSDELTASSUPPORT (0x8, MUST), NEGOTIATEORDERSUPPORT (0x2, MUST)
+        // Order support: 32 bytes (no primary drawing orders are supported, so this array MUST be initialized to all zeros, use 0x01 for TRUE).
+        (byte)0x00, // TS_NEG_DSTBLT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_PATBLT_INDEX: FALSE  
+        (byte)0x00, // TS_NEG_SCRBLT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_MEMBLT_INDEX: FALSE  
+        (byte)0x00, // TS_NEG_MEM3BLT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_ATEXTOUT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_AEXTTEXTOUT_INDEX: FALSE
+        (byte)0x00, // TS_NEG_DRAWNINEGRID_INDEX: FALSE
+        (byte)0x00, // TS_NEG_LINETO_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MULTI_DRAWNINEGRID_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_OPAQUERECT_INDEX: FALSE
+        (byte)0x00, // TS_NEG_SAVEBITMAP_INDEX: FALSE
+        (byte)0x00, // TS_NEG_WTEXTOUT_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MEMBLT_R2_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MEM3BLT_R2_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MULTIDSTBLT_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MULTIPATBLT_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MULTISCRBLT_INDEX: FALSE
+        (byte)0x00, // TS_NEG_MULTIOPAQUERECT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_FAST_INDEX_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_POLYGON_SC_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_POLYGON_CB_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_POLYLINE_INDEX: TRUE 
+        (byte)0x00, // Unused: 0 
+        (byte)0x00, // TS_NEG_FAST_GLYPH_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_ELLIPSE_SC_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_ELLIPSE_CB_INDEX: FALSE
+        (byte)0x00, // TS_NEG_INDEX_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_WEXTTEXTOUT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_WLONGTEXTOUT_INDEX: FALSE 
+        (byte)0x00, // TS_NEG_WLONGEXTTEXTOUT_INDEX: FALSE
+        (byte)0x00, // Unused: 0 
+        (byte)0x00, (byte)0x00, // Text flags (ignored): 0  (LE)
+        (byte)0x00, (byte)0x00, // Order support extra flags: 0 (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 4 bytes
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Desktop save size (ignored): 0 (assumed to be 230400 bytes (480*480, 0x38400, LE))  
+        (byte)0x00, (byte)0x00, // Padding 2 bytes 
+        (byte)0x00, (byte)0x00, // Padding 2 bytes
+        (byte)0xe4, (byte)0x04, // Text ANSI Code Page: 1252,  ANSI - Latin I (0x04e4, LE) 
+        (byte)0x00, (byte)0x00, // Padding 2 bytes
+        
+        //
+        // Bitmap Cache Rev. 2 Capability Set (40 bytes), see http://msdn.microsoft.com/en-us/library/cc240560.aspx
+        (byte)0x13, (byte)0x00, // capability set type: CAPSTYPE_BITMAPCACHE_REV2 (19) (LE) 
+        (byte)0x28, (byte)0x00, // length of capability set: 40 bytes (LE)
+        (byte)0x00, (byte)0x00, // Cache flags: 0 (LE) 
+        (byte)0x00, // Padding 1 byte 
+        (byte)0x00, // Number of cell caches: 0 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache0 cell info: 0 (LE) 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache1 cell info: 0 (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache2 cell info: 0 (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache3 cell info: 0 (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache4 cell info: 0 (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 12 bytes 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding
+        
+        //
+        // Color Table Cache Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc241564.aspx
+        (byte)0x0a, (byte)0x00, // capability set type: CAPSTYPE_COLORCACHE (10) (LE)
+        (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE)
+        (byte)0x06, (byte)0x00, // Color table cache size (must be ignored during capability exchange and is assumed to be 0x0006): 6 (LE)
+        (byte)0x00, (byte)0x00, // Padding 2 bytes
+        
+        //
+        // Window Activation Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240569.aspx
+        (byte)0x07, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION (7) (LE) 
+        (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE)
+        (byte)0x00, (byte)0x00, // helpKeyFlag (should be set to FALSE (0)): FALSE (0, LE)
+        (byte)0x00, (byte)0x00, // helpKeyIndexFlag (should be set to FALSE (0)): FALSE (0, LE)
+        (byte)0x00, (byte)0x00, // helpExtendedKeyFlag (should be set to FALSE (0)): FALSE (0, LE)
+        (byte)0x00, (byte)0x00, // windowManagerKeyFlag (should be set to FALSE (0)): FALSE (0, LE)
+        
+        //
+        // Control Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240568.aspx
+        (byte)0x05, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION (7)
+        (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE)
+        (byte)0x00, (byte)0x00, // controlFlags (should be set to 0): 0 (LE)
+        (byte)0x00, (byte)0x00, // remoteDetachFlag (should be set to 0): 0 (LE)
+        (byte)0x02, (byte)0x00, // controlInterest (should be set to CONTROLPRIORITY_NEVER): CONTROLPRIORITY_NEVER (2) (LE)
+        (byte)0x02, (byte)0x00, // detachInterest (should be set to CONTROLPRIORITY_NEVER): CONTROLPRIORITY_NEVER (2) (LE)
+        
+        //
+        // Pointer Capability Set (10 bytes), see http://msdn.microsoft.com/en-us/library/cc240562.aspx
+        (byte)0x08, (byte)0x00, // capability set type: CAPSTYPE_POINTER (8, LE)
+        (byte)0x0a, (byte)0x00, // length of capability set: 10 bytes (LE)
+        (byte)0x00, (byte)0x00, // colorPointerFlag: FALSE (LE)
+        (byte)0x00, (byte)0x00, // colorPointerCacheSize: 0 (LE)
+        (byte)0x14, (byte)0x00, // pointerCacheSize: 20 (LE)
+        
+        //
+        // Share Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240570.aspx
+        (byte)0x09, (byte)0x00, // capability set type: CAPSTYPE_SHARE (9, LE)
+        (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE)
+        (byte)0x00, (byte)0x00, // nodeID (must be set to 0 by client): 0 (LE)
+        (byte)0x00, (byte)0x00, // Padding 2 bytes (LE)
+        
+        //
+        // Input Capability Set (88 bytes), see http://msdn.microsoft.com/en-us/library/cc240563.aspx
+        (byte)0x0d, (byte)0x00, // capability set type:  CAPSTYPE_INPUT (13, LE)
+        (byte)0x58, (byte)0x00, // length of capability set: 88 bytes (LE)
+        (byte)0x35, (byte)0x00, // inputFlags: 0x0035  (LE),  INPUT_FLAG_FASTPATH_INPUT2 (0x20), INPUT_FLAG_VKPACKET (0x10), INPUT_FLAG_MOUSEX (0x4), INPUT_FLAG_SCANCODES (0x1)
+        (byte)0x00, (byte)0x00, // Padding 2 bytes 
+        (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, // keyboardLayout: "US" keyboard layout (0x000409, LE) 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardType: unknown (LE) 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardSubType: unknown (LE) 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardFunctionKey: unknown (LE)
+        // imeFileName: "", (64 bytes, including trailing NULL characters, UCS2) 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+        
+        //
+        // Brush Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240564.aspx
+        (byte)0x0f, (byte)0x00, // capability set type: CAPSTYPE_BRUSH (15, LE)
+        (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // brushSupportLevel: BRUSH_DEFAULT (0x0, LE)
+        
+        //
+        // Sound Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240552.aspx
+        (byte)0x0c, (byte)0x00, // capability set type: CAPSTYPE_SOUND (12, LE)
+        (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // soundFlags: 0x0000 (LE) // SOUND_FLAG_BEEPS (0x1)   
+        
+        //
+        // Font Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240571.aspx
+        (byte)0x0e, (byte)0x00, 
+        (byte)0x08, (byte)0x00, 
+        (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, 
+        
+        //
+        // Offscreen Bitmap Cache Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240550.aspx
+        (byte)0x11, (byte)0x00, // capability set type: CAPSTYPE_OFFSCREENCACHE (17, LE)
+        (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE)
+        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // offscreenSupportLevel: FALSE (LE) 
+        (byte)0x00, (byte)0x00, // offscreenCacheSize: 0 (LE)
+        (byte)0x00, (byte)0x00, // offscreenCacheEntries: 0 (LE)
+        
+        //
+        // Glyph Cache Capability Set (52 bytes), see http://msdn.microsoft.com/en-us/library/cc240565.aspx
+        (byte)0x10, (byte)0x00, // capability set type: CAPSTYPE_OFFSCREENCACHE (16, LE)
+        (byte)0x34, (byte)0x00, // length of capability set: 52 bytes (LE)
+        // Glyph Cache (40 bytes)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) 
+        (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x10, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x20, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x40, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x80, (byte)0x00, // CacheMaximumCellSize: 4 (LE)
+        (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE)
+        (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 4 (LE)
+        (byte)0x40, (byte)0x00, // CacheEntries: 64 (LE)
+        (byte)0x00, (byte)0x08, // CacheMaximumCellSize: 2048 (LE)
+        // FragCache
+        (byte)0x00, (byte)0x01, // CacheEntries: 256 (LE)
+        (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 256 (LE)
+        // 
+        (byte)0x00, (byte)0x00, // GlyphSupportLevel: GLYPH_SUPPORT_NONE (0x0, LE)  
+        (byte)0x00, (byte)0x00, // Padding 2 bytes   
+    };
+    /* @formatter:on */
+
+    RdpState rdpState = new RdpState();
+    ScreenDescription screenDescription = new ScreenDescription();
+    screenDescription.setFramebufferSize(1024, 768);
+    
+    rdpState.serverShareId = 0x000103ea;
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {}));
+    Element confirm_active = new ClientConfirmActivePDU("confirm_active", screenDescription, rdpState);
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, confirm_active, sink);
+    pipeline.link("source", "confirm_active", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java
new file mode 100644
index 0000000..4c7dcfc
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java
@@ -0,0 +1,55 @@
+// 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;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240589.aspx
+ */
+public class ClientFastPathPDU extends BaseElement {
+
+  public ClientFastPathPDU(String id) {
+    super(id);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    if (buf.length > 32767-3)
+      throw new RuntimeException("Packet is too long: " + buf + ".");
+
+    ByteBuffer data = new ByteBuffer(6);
+
+    // FastPath, 1 event, no checksum, not encrypted
+    data.writeByte(0x4);
+
+    // Length of full packet, including length field, in network order.
+    // Topmost bit of first byte indicates that field has 2 bytes
+    data.writeShort((1 + 2 + buf.length) | 0x8000);
+    data.trimAtCursor();
+
+    buf.prepend(data);
+
+    pushDataToAllOuts(buf);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java
new file mode 100644
index 0000000..f5efc2c
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java
@@ -0,0 +1,456 @@
+// 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;
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240475.aspx
+ */
+public class ClientInfoPDU extends OneTimeSwitch {
+
+  public static final int INFO_MOUSE = 0x1;
+  public static final int INFO_DISABLECTRLALTDEL = 0x2;
+  public static final int INFO_UNICODE = 0x10;
+
+  public static final int INFO_MAXIMIZESHELL = 0x20;
+  public static final int INFO_LOGONNOTIFY = 0x40;
+  public static final int INFO_ENABLEWINDOWSKEY = 0x100;
+  public static final int INFO_MOUSE_HAS_WHEEL = 0x00020000;
+  public static final int INFO_NOAUDIOPLAYBACK = 0x00080000;
+
+  public static final int PERF_DISABLE_WALLPAPER = 0x1;
+  public static final int PERF_DISABLE_FULLWINDOWDRAG = 0x2;
+  public static final int PERF_DISABLE_MENUANIMATIONS = 0x4;
+
+  protected byte[] userName = "".getBytes(RdpConstants.CHARSET_16);
+  protected byte[] password = "".getBytes(RdpConstants.CHARSET_16); // No effect
+  protected byte[] alternateShell = "".getBytes(RdpConstants.CHARSET_16);
+  protected byte[] domain = "".getBytes(RdpConstants.CHARSET_16);
+  protected byte[] workingDir = "".getBytes(RdpConstants.CHARSET_16);
+  protected byte[] clientAddress = "192.168.0.100".getBytes(RdpConstants.CHARSET_16);
+  protected byte[] clientDir = "C:\\Windows\\System32\\mstscax.dll".getBytes(RdpConstants.CHARSET_16);
+
+  protected String standardTimeZoneName = "EET, Standard Time";
+  protected String daylightTimeZoneName = "EET, Summer Time";
+  protected int standardTimeZoneBias = 0; /* in minutes */
+  protected int daylightTimeZoneBias = 60; /* in minutes */
+
+  public ClientInfoPDU(String id, String userName) {
+    super(id);
+    this.userName = userName.getBytes(RdpConstants.CHARSET_16);
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    throw new RuntimeException("Unexpected packet: " + buf + ".");
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    // Length of packet
+    ByteBuffer buf = new ByteBuffer(1024, true);
+
+    // MCS Send Data Request PDU 
+    buf.writeByte(0x64);
+    
+    // Initiator: 0x03 + 1001 = 1004
+    buf.writeShort(3); 
+    
+    // Channel ID: 1003
+    buf.writeShort(1003);
+    
+    // Data priority: high, segmentation: begin | end (0x40 | 0x20 | 0x10 = 0x70)
+    buf.writeByte(0x70);
+    
+    // User data length: (variable length field)
+    int length=224+userName.length + password.length+alternateShell.length+domain.length+workingDir.length+clientAddress.length+clientDir.length;
+    buf.writeShort(length | 0x8000);
+    
+    // Flags: SEC_INFO_PKT (0x4000)
+    buf.writeShort(0x4000);
+     
+    // TS_SECURITY_HEADER::flagsHi - ignored
+    buf.writeShort(0x0000); 
+
+    // Codepage: 0 (UNKNOWN, LE) (use  0x04090409  (1033,1033) for EN_US)  
+    buf.writeIntLE(0x0000);
+     
+    // Flags
+    buf.writeIntLE(INFO_MOUSE | INFO_DISABLECTRLALTDEL | INFO_UNICODE |  
+    INFO_MAXIMIZESHELL | INFO_LOGONNOTIFY  |  INFO_ENABLEWINDOWSKEY |
+    INFO_MOUSE_HAS_WHEEL | INFO_NOAUDIOPLAYBACK );
+    
+    //
+    // Lengths
+    //
+        
+    // cbDomain length: 0 bytes (LE) (NOT including size of mandatory NULL terminator)
+    buf.writeShortLE(domain.length); 
+    
+    // cbUserName length: 16 bytes (0x10, LE) (NOT including size of mandatory NULL terminator)
+    buf.writeShortLE(userName.length);
+     
+    // cbPassword length: (LE) (NOT including size of mandatory NULL terminator)
+    buf.writeShortLE(password.length);
+    
+    // cbAlternateShell:  (LE) (NOT including size of mandatory NULL terminator)
+    buf.writeShortLE(alternateShell.length);
+    
+    // cbWorkingDir: (LE) (NOT including size of mandatory NULL terminator)
+    buf.writeShortLE(workingDir.length);
+    
+    //
+    // Values
+    //
+        
+    // Domain: (UCS2), see cbDomain
+    buf.writeBytes(domain);
+    buf.writeShort(0);
+    
+    // User name: (UCS2), see cbUserName
+    buf.writeBytes(userName);
+    buf.writeShort(0);
+    
+    // Password: (UCS2), see cbPassword
+    buf.writeBytes(password);
+    buf.writeShort(0);
+    
+    // Alternate shell: (UCS2), see cbAlternateShell
+    buf.writeBytes(alternateShell);
+    buf.writeShort(0);
+    
+    // Working directory: (UCS2), see cbWorkingDir
+    buf.writeBytes(workingDir);
+    buf.writeShort(0);
+    
+    // Client address family: 2 (AF_INET, LE)
+    buf.writeShortLE(2);
+    
+    // cbClientAddress: ( LE) (including the size of the mandatory NULL terminator)
+    buf.writeShortLE(clientAddress.length+2);
+    
+    // Client address: (UCS2) 
+    buf.writeBytes(clientAddress);
+    buf.writeShort(0);
+    
+    // cbClientDir: 64 bytes (0x40, LE) (including the size of the mandatory NULL terminator)
+    buf.writeShortLE(clientDir.length+2);
+    
+    // Client directory: (UCS2)
+    buf.writeBytes(clientDir);
+    buf.writeShort(0);
+    
+    //
+    // Client time zone:
+    //
+    
+    // Bias: 0 minutes (LE)
+    buf.writeIntLE(0); 
+    
+    // Standard name: "EET, Standard Time" (fixed string: 64 bytes, UCS2)
+    buf.writeFixedString(62, standardTimeZoneName, RdpConstants.CHARSET_16);
+    buf.writeShort(0);
+    
+    // Standard date
+    buf.writeBytes(new byte[] { 
+        // wYear: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMonth: unknown (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wDayOfWeek: Sunday (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wDay: unknown (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wHour: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMinute: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wSecond: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMilliseconds: 0
+        (byte) 0x00, (byte) 0x00,
+        
+    });
+    
+    // StandardBias: 0 minutes (LE)
+    buf.writeIntLE(standardTimeZoneBias); 
+    
+    // Daylight name: "EET, Summer Time" (fixed string: 64 bytes, UCS2)
+    buf.writeFixedString(62, daylightTimeZoneName, RdpConstants.CHARSET_16);
+    buf.writeShort(0);
+    
+    // Daylight date
+    buf.writeBytes(new byte[] { 
+        // wYear: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMonth: unknown (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wDayOfWeek: Sunday (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wDay: unknown (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wHour: 0 (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wMinute: 0 (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wSecond: 0 (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wMilliseconds: 0
+        (byte) 0x00, (byte) 0x00,
+        
+    });
+    
+    // Daylight bias: 60 minutes (LE)
+    buf.writeIntLE(daylightTimeZoneBias);
+     
+    // Client session ID: 0x00000000 (LE)
+    buf.writeIntLE(0);
+    
+    // Performance flags: 0x7 (LE) = PERF_DISABLE_WALLPAPER (0x1), PERF_DISABLE_FULLWINDOWDRAG (0x2), PERF_DISABLE_MENUANIMATIONS (0x4)
+    buf.writeIntLE(PERF_DISABLE_WALLPAPER | PERF_DISABLE_FULLWINDOWDRAG | PERF_DISABLE_MENUANIMATIONS);
+    
+    // cbAutoReconnectCookie: 0 bytes (LE)
+    buf.writeShortLE(0);
+
+    // Trim buffer to actual length of data written
+    buf.length = buf.cursor;
+
+    pushDataToOTOut(buf);
+
+    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");
+
+    /* @formatter:off */
+    byte[] packet = new byte[] {
+        
+        // TPKT
+        (byte) 0x03, (byte) 0x00,
+
+        // TPKT length: 343 bytes
+        (byte) 0x01, (byte) 0x57,
+        
+        // X224 Data PDU
+        (byte) 0x02, (byte) 0xf0, (byte) 0x80,
+        
+        
+        // MCS Send Data Request PDU 
+        (byte) 0x64, 
+        
+        // Initiator: 0x03 + 1001 = 1004
+        (byte) 0x00, (byte) 0x03,
+        
+        // Channel ID: 1003 (IO Channel)
+        (byte) 0x03, (byte) 0xeb,
+        
+        // Data priority: high, segmentation: begin | end (0x40 | 0x20 | 0x10 = 0x70)
+        (byte) 0x70, 
+        
+        // User data length: 328  (0x148) bytes, variable length field 
+        (byte) 0x81, (byte) 0x48, 
+        
+        // Flags: SEC_INFO_PKT (0x4000)
+        (byte) 0x40, (byte) 0x00, 
+
+        // TS_SECURITY_HEADER::flagsHi - ignored
+        (byte) 0x00, (byte) 0x00,
+        
+        // Codepage: 0 (UNKNOWN, LE) (use  0x04090409  (1033,1033) for EN_US)  
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        
+        // Flags: 0xa0173 (LE), INFO_MOUSE (0x1), INFO_DISABLECTRLALTDEL (0x2), INFO_UNICODE (0x10), 
+        // INFO_MAXIMIZESHELL (0x20), INFO_LOGONNOTIFY (0x40), INFO_ENABLEWINDOWSKEY (0x100),
+        // INFO_MOUSE_HAS_WHEEL (0x00020000), INFO_NOAUDIOPLAYBACK (0x00080000), 
+        (byte) 0x73, (byte) 0x01, (byte) 0x0a, (byte) 0x00, 
+        
+        // Lengths
+        
+        // cbDomain length: 0 bytes (LE) (NOT including size of mandatory NULL terminator)
+        (byte) 0x00, (byte) 0x00, 
+        
+        // cbUserName length: 16 bytes (0x10, LE) (NOT including size of mandatory NULL terminator)
+        (byte) 0x10, (byte) 0x00,
+        
+        // cbPassword length: 0 bytes (LE) (NOT including size of mandatory NULL terminator)
+        (byte) 0x00, (byte) 0x00,
+        
+        // cbAlternateShell:  0 bytes (LE) (NOT including size of mandatory NULL terminator)
+        (byte) 0x00, (byte) 0x00, 
+        
+        // cbWorkingDir: 0 bytes (LE) (NOT including size of mandatory NULL terminator)
+        (byte) 0x00, (byte) 0x00,
+        
+        // Values
+        
+        // Domain: "" (UCS2), see cbDomain
+        (byte) 0x00, (byte) 0x00, 
+        
+        // User name: "vlisivka" (UCS2), see cbUserName
+        (byte) 0x76, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x73, (byte) 0x00, 
+        (byte) 0x69, (byte) 0x00, (byte) 0x76, (byte) 0x00, (byte) 0x6b, (byte) 0x00, (byte) 0x61, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, 
+        
+        // Password: "" (UCS2), see cbPassword
+        (byte) 0x00, (byte) 0x00, 
+        
+        // Alternate shell: "" (UCS2), see cbAlternateShell
+        (byte) 0x00, (byte) 0x00, 
+        
+        // Working directory: "" (UCS2), see cbWorkingDir
+        (byte) 0x00, (byte) 0x00, 
+        
+        // Client address family: 2 (AF_INET, LE)
+        (byte) 0x02, (byte) 0x00, 
+        
+        // cbClientAddress = 28 bytes (0x1c, LE) (including the size of the mandatory NULL terminator)
+        (byte) 0x1c, (byte) 0x00, 
+        
+        // Client address: "192.168.0.100" (UCS2) 
+        (byte) 0x31, (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x2e, (byte) 0x00,
+        (byte) 0x31, (byte) 0x00, (byte) 0x36, (byte) 0x00, (byte) 0x38, (byte) 0x00, (byte) 0x2e, (byte) 0x00,
+        (byte) 0x30, (byte) 0x00, (byte) 0x2e, (byte) 0x00, (byte) 0x31, (byte) 0x00, (byte) 0x30, (byte) 0x00, 
+        (byte) 0x30, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        
+        // cbClientDir: 64 bytes (0x40, LE) (including the size of the mandatory NULL terminator)
+        (byte) 0x40, (byte) 0x00,
+        
+        // Client directory: "C:\Windows\System32\mstscax.dll" (UCS2)
+        (byte) 0x43, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x57, (byte) 0x00, 
+        (byte) 0x69, (byte) 0x00, (byte) 0x6e, (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x6f, (byte) 0x00, 
+        (byte) 0x77, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x53, (byte) 0x00, 
+        (byte) 0x79, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x65, (byte) 0x00, 
+        (byte) 0x6d, (byte) 0x00, (byte) 0x33, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x5c, (byte) 0x00, 
+        (byte) 0x6d, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x73, (byte) 0x00, 
+        (byte) 0x63, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x78, (byte) 0x00, (byte) 0x2e, (byte) 0x00, 
+        (byte) 0x64, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        
+        //
+        // Client time zone:
+        
+        // Bias: 0 minutes (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        
+        // Standard name: "EET, Standard Time" (fixed string: 64 bytes, UCS2)
+        (byte) 0x45, (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x2c, (byte) 0x00, 
+        (byte) 0x20, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61, (byte) 0x00, 
+        (byte) 0x6e, (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x72, (byte) 0x00, 
+        (byte) 0x64, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x69, (byte) 0x00, 
+        (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        
+        //
+        // Standard date
+        // wYear: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMonth: unknown (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wDayOfWeek: Sunday (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wDay: unknown (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wHour: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMinute: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wSecond: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMilliseconds: 0
+        (byte) 0x00, (byte) 0x00,
+        
+        // StandardBias: 0 minutes (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        
+        // Daylight name: "EET, Summer Time" (fixed string: 64 bytes, UCS2)
+        (byte) 0x45, (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x2c, (byte) 0x00, 
+        (byte) 0x20, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x75, (byte) 0x00, (byte) 0x6d, (byte) 0x00, 
+        (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x20, (byte) 0x00, 
+        (byte) 0x54, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        
+        // Daylight date
+        // wYear: 0 (LE)
+        (byte) 0x00, (byte) 0x00, 
+        // wMonth: unknown (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wDayOfWeek: Sunday (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wDay: unknown (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wHour: 0 (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wMinute: 0 (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wSecond: 0 (LE)
+        (byte) 0x00, (byte) 0x00,
+        // wMilliseconds: 0
+        (byte) 0x00, (byte) 0x00,
+        
+        // Daylight bias: 60 minutes (LE)
+        (byte) 0x3c, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        
+        
+        // Client session ID: 0x00000000 (LE)
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        
+        // Performance flags: 0x7 (LE) = PERF_DISABLE_WALLPAPER (0x1), PERF_DISABLE_FULLWINDOWDRAG (0x2), PERF_DISABLE_MENUANIMATIONS (0x4)
+        (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
+        
+        // cbAutoReconnectCookie: 0 bytes (LE)
+        (byte) 0x00, (byte) 0x00, 
+    };
+    /* @formatter:on */
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+    Element todo = new ClientInfoPDU("client_info", "vlisivka");
+    Element x224 = new ClientX224DataPdu("x224");
+    Element tpkt = new ClientTpkt("tpkt");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, todo, x224, tpkt, sink, mainSink);
+    pipeline.link("source", "client_info", "mainSink");
+    pipeline.link("client_info >" + OTOUT, "x224", "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java
new file mode 100644
index 0000000..10a7eff
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java
@@ -0,0 +1,102 @@
+// 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;
+/**
+ * @see http://msdn.microsoft.com/en-us/library/cc240684.aspx
+ */
+public class ClientMCSAttachUserRequest extends OneTimeSwitch {
+
+  public ClientMCSAttachUserRequest(String id) {
+    super(id);
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    throw new RuntimeException("Unexpected packet: " + buf + ".");
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    int length = 1;
+    ByteBuffer buf = new ByteBuffer(length, true);
+
+    buf.writeByte(0x28); // AttachUserRequest
+
+    pushDataToOTOut(buf);
+
+    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");
+
+    /* @formatter:off */
+    byte[] packet = new byte[] {
+
+        0x03, 0x00, 0x00, 0x08,  //  TPKT Header (length = 8 bytes)
+        0x02, (byte) 0xf0, (byte) 0x80,  //  X.224 Data TPDU
+        
+        // PER encoded (ALIGNED variant of BASIC-PER) PDU contents:
+        0x28, 
+        
+        // 0x28:
+        // 0 - --\
+        // 0 -   |
+        // 1 -   | CHOICE: From DomainMCSPDU select attachUserRequest (10) 
+        // 0 -   | of type AttachUserRequest
+        // 1 -   |
+        // 0 - --/
+        // 0 - padding
+        // 0 - padding
+
+    };
+    /* @formatter:on */
+
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+    Element todo = new ClientMCSAttachUserRequest("TODO");
+    Element x224 = new ClientX224DataPdu("x224");
+    Element tpkt = new ClientTpkt("tpkt");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet));
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, todo, x224, tpkt, sink, mainSink);
+    pipeline.link("source", "TODO", "mainSink");
+    pipeline.link("TODO >" + OTOUT, "x224", "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java
new file mode 100644
index 0000000..f082539
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java
@@ -0,0 +1,222 @@
+// 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;
+
+/**
+ * The MCS Channel Join Request PDUs are sent sequentially. The first PDU is
+ * sent after receiving the MCS Attach User Confirm PDU and subsequent PDUs are
+ * sent after receiving the MCS Channel Join Confirm PDU for the previous
+ * request. Sending of the MCS Channel Join Request PDUs MUST continue until all
+ * channels have been successfully joined.
+ * 
+ * @see http://msdn.microsoft.com/en-us/library/cc240686.aspx
+ */
+public class ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs extends OneTimeSwitch {
+
+  private static final int MCS_CHANNEL_CONFIRM_PDU = 15;
+
+  protected int[] channels;
+  protected int channelRequestsSent = 0;
+
+  protected RdpState state;
+
+  public ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs(String id, int[] channels, RdpState state) {
+    super(id);
+    this.channels = channels;
+    this.state=state;
+  }
+
+  @Override
+  protected void handleOneTimeData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    // Parse channel confirm response
+    int typeAndFlags = buf.readUnsignedByte();
+    int type = typeAndFlags >> 2;
+    // int flags = typeAndFlags & 0x3;
+
+    if (type != MCS_CHANNEL_CONFIRM_PDU)
+      throw new RuntimeException("["+this+"] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 15, actual value: " + type + ", data: " + buf + ".");
+
+    int rtSuccess = buf.readUnsignedByte() >> 4;
+    if (rtSuccess != 0)
+      throw new RuntimeException("["+this+"] ERROR: Cannot connect to channel: request failed. Error code: " + rtSuccess + ", channel ID: " + channels[channelRequestsSent - 1]
+          + ", data: " + buf + ".");
+
+    // Initiator and requested fields MAY be ignored, however, the channelId
+    // field MUST be examined. If the value of the channelId field does not
+    // correspond with the value of the channelId field sent in the previous MCS
+    // Channel Join Request PDU the connection SHOULD be dropped.
+
+    // Initiator: 1007 (6+1001)
+    // int initator=buf.readUnsignedShort();
+    buf.skipBytes(2);
+
+    // Requested channel
+    // int requestedChannel=buf.readUnsignedShort();
+    buf.skipBytes(2);
+
+    // Actual channel
+    int actualChannel = buf.readUnsignedShort();
+    if (actualChannel != channels[channelRequestsSent - 1])
+      throw new RuntimeException("Unexpeceted channeld ID returned. Expected channeld ID: " + channels[channelRequestsSent - 1] + ", actual channel ID: "
+          + actualChannel + ", data: " + buf + ".");
+    
+    state.channelJoined(actualChannel);
+    
+    buf.unref();
+    
+
+    if (channelRequestsSent < channels.length)
+      sendChannelRequest(channels[channelRequestsSent++]);
+    else
+      switchOff();
+  }
+
+  @Override
+  protected void onStart() {
+    super.onStart();
+
+    sendChannelRequest(channels[channelRequestsSent++]);
+
+    // Switch off after receiving response(s)
+  }
+
+  private void sendChannelRequest(int channel) {
+    ByteBuffer buf = new ByteBuffer(5, true);
+
+    buf.writeByte(0x38); // Channel Join request
+
+    buf.writeShort(0x03); // ChannelJoinRequest::initiator: 1004
+    buf.writeShort(channel);
+
+    pushDataToOTOut(buf);
+  }
+
+  /**
+   * Example.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc240834.aspx
+   */
+  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[] clientRequestPacket = new byte[] {
+        0x03, 0x00, 0x00, 0x0c,  //  TPKT Header (length = 12 bytes)
+        0x02, (byte) 0xf0, (byte) 0x80,  //  X.224 Data TPDU
+        
+        // PER encoded (ALIGNED variant of BASIC-PER) PDU contents:
+        0x38, 0x00, 0x03, 0x03, (byte) 0xef, 
+
+         // 0x38:
+         // 0 - --\
+         // 0 -   |
+         // 1 -   | CHOICE: From DomainMCSPDU select channelJoinRequest (14) 
+         // 1 -   | of type ChannelJoinRequest
+         // 1 -   |
+         // 0 - --/
+         // 0 - padding
+         // 0 - padding
+
+         // 0x00:
+         // 0 - --\
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         //       | ChannelJoinRequest::initiator = 0x03 + 1001 = 1004
+         // 0x03: |
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 0 -   | 
+         // 1 -   | 
+         // 1 -   | 
+         // 0 - --/
+
+         // 0x03:
+         // 0 - --\
+         // 0 -   |
+         // 0 -   |
+         // 0 -   |
+         // 0 -   |
+         // 0 -   |
+         // 1 -   |
+         // 1 -   |
+         //       | ChannelJoinRequest::channelId = 0x03ef = 1007
+         // 0xef: |
+         // 1 -   |
+         // 1 -   |
+         // 1 -   |
+         // 0 -   |
+         // 1 -   |
+         // 1 -   |
+         // 1 -   |
+         // 1 - --/
+    };
+    
+    byte[] serverResponsePacket = new byte[] {
+        // MCS Channel Confirm
+    (byte)0x3e, 
+    
+    // result: rt-successful (0)
+    (byte)0x00, 
+    
+    // Initiator: 1007 (6+1001)
+    (byte)0x00, (byte)0x06, 
+    
+    // Requested channel
+    (byte)0x03, (byte)0xef,
+    
+    // Actual channel
+    (byte)0x03, (byte)0xef,
+    };
+    /* @formatter:on */
+
+    RdpState rdpState = new RdpState();
+    MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(serverResponsePacket, new byte[] { 1, 2, 3 }));
+    Element todo = new ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs("channels", new int[] { 1007 }, rdpState);
+    Element x224 = new ClientX224DataPdu("x224");
+    Element tpkt = new ClientTpkt("tpkt");
+    Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(clientRequestPacket));
+    Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }));
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source, todo, x224, tpkt, sink, mainSink);
+    pipeline.link("source", "channels", "mainSink");
+    pipeline.link("channels >" + OTOUT, "x224", "tpkt", "sink");
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}


[3/9] Remote Desktop Protocol(RDP) client that is suitable for including in the console VM. The client renders RDP to a window created by Java. It is important for Hyper-V support, because Hyper-V provides access to the consoles of VMs that it is running

Posted by de...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java
new file mode 100644
index 0000000..832c731
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java
@@ -0,0 +1,826 @@
+// 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 streamer;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class represents a slice in a buffer.
+ */
+public class ByteBuffer {
+
+  public static final String SEQUENCE_NUMBER = "seq";
+
+  public byte data[];
+  public int offset = 0;
+  public int length = 0;
+  public int cursor = 0;
+
+  private int refCount = 1;
+  private ByteBuffer parentByteBuffer = null;
+
+  private Order order;
+
+  /**
+   * Create buffer of size no less than length. Buffer can be a bit larger than
+   * length. Offset also can be set to non-zero value to leave some place for
+   * future headers.
+   */
+  public ByteBuffer(int minLength) {
+    // Get buffer of acceptable size from buffer pool
+    this.data = BufferPool.allocateNewBuffer(minLength);
+    this.offset = 0;
+    this.length = minLength;
+  }
+
+  public ByteBuffer(byte data[]) {
+    if (data == null)
+      throw new NullPointerException("Data must be non-null.");
+
+    this.data = data;
+    this.offset = 0;
+    this.length = data.length;
+  }
+
+  public ByteBuffer(byte[] data, int offset, int length) {
+    if (data == null)
+      throw new NullPointerException("Data must be non-null.");
+
+    this.data = data;
+    this.offset = offset;
+    this.length = length;
+  }
+
+  /**
+   * Create byte buffer of requested size with some space reserved for future
+   * headers.
+   */
+  public ByteBuffer(int minLength, boolean reserveSpaceForHeader) {
+    // Get buffer of acceptable size from buffer pool
+    this.data = BufferPool.allocateNewBuffer(128 + minLength);
+    this.offset = 128; // 100 bytes should be enough for headers
+    this.length = minLength;
+  }
+
+  /**
+   * Create empty buffer with given order only.
+   */
+  public ByteBuffer(Order order) {
+    this.order = order;
+  }
+
+  public void setOrder(Order order) {
+    this.order = order;
+  }
+
+  public Order getOrder() {
+    return order;
+  }
+
+  @Override
+  public String toString() {
+    return toString(100);
+  }
+
+  /**
+   * Return string representation of this byte buffer.
+   * 
+   * @param maxLength
+   *          number of bytes to show in string
+   */
+  public String toString(int maxLength) {
+    return "ByteRange(){offset=" + offset + ", length=" + length + ", cursor=" + cursor + ", data=" + ((data == null) ? "null" : toHexString(maxLength))
+        + ((metadata == null || metadata.size() == 0) ? "" : ", metadata=" + metadata) + "}";
+  }
+
+  /**
+   * Return string representation of this byte buffer as hexadecimal numbers,
+   * e.g. "[0x01, 0x02]".
+   * 
+   * @param maxLength
+   *          number of bytes to show in string
+   */
+  public String toHexString(int maxLength) {
+    StringBuilder builder = new StringBuilder(maxLength * 6);
+    builder.append('[');
+    for (int i = 0; i < maxLength && i < length; i++) {
+      if (i > 0)
+        builder.append(", ");
+      int b = data[offset + i] & 0xff;
+      builder.append("0x" + ((b < 16) ? "0" : "") + Integer.toString(b, 16));
+    }
+    builder.append(']');
+    return builder.toString();
+  }
+
+  /**
+   * Return string representation of this byte buffer as hexadecimal numbers,
+   * e.g. "01 02".
+   * 
+   * @param maxLength
+   *          number of bytes to show in string
+   */
+  public String toPlainHexString(int maxLength) {
+    StringBuilder builder = new StringBuilder(maxLength * 3);
+    for (int i = 0; i < maxLength && i < length; i++) {
+      if (i > 0)
+        builder.append(" ");
+      int b = data[offset + i] & 0xff;
+      builder.append(((b < 16) ? "0" : "") + Integer.toString(b, 16));
+    }
+    return builder.toString();
+  }
+
+  public void dump() {
+    System.out.println(toString(length));
+  }
+
+  public void extend(int newLength) {
+    if (data.length < newLength)
+      Arrays.copyOf(data, newLength);
+  }
+
+  public void ref() {
+    refCount++;
+  }
+
+  public void unref() {
+    refCount--;
+
+    if (refCount == 0) {
+
+      if (parentByteBuffer != null) {
+        parentByteBuffer.unref();
+        parentByteBuffer = null;
+      } else {
+        // Return buffer to buffer pool
+        BufferPool.recycleBuffer(data);
+      }
+
+      data = null;
+    }
+
+  }
+
+  public boolean isSoleOwner() {
+    return refCount == 1 && (parentByteBuffer == null);
+  }
+
+  /**
+   * Create shared lightweight copy of part of this buffer.
+   */
+  public ByteBuffer slice(int offset, int length, boolean copyMetadata) {
+    ref();
+
+    if (this.length < (offset + length))
+      throw new RuntimeException("Length of region is larger that length of this buffer. Buffer length: " + this.length + ", offset: " + offset
+          + ", new region length: " + length + ".");
+
+    ByteBuffer slice = new ByteBuffer(data, this.offset + offset, length);
+
+    if (copyMetadata && this.metadata != null)
+      slice.metadata = new HashMap<String, Object>(metadata);
+
+    return slice;
+  }
+
+  private Map<String, Object> metadata = null;
+
+  public Object putMetadata(String key, Object value) {
+    if (metadata == null)
+      metadata = new HashMap<String, Object>();
+    return metadata.put(key, value);
+  }
+
+  public Object getMetadata(String key) {
+    return (metadata != null) ? metadata.get(key) : null;
+  }
+
+  /**
+   * Create new buffer, which holds data from both buffers. Expensive operation.
+   * 
+   * @TODO if only one reference to this ByteBuffer exists, then extend this
+   *       buffer instead of creating new buffer
+   * @TODO support list of buffers to avoid expensive joins until absolute
+   *       necessary
+   */
+  public ByteBuffer join(ByteBuffer buf) {
+    // Extend byte array for new data
+    int newLength = length + buf.length;
+    byte newData[] = new byte[newLength];
+
+    // Copy data from our buffer
+    System.arraycopy(data, offset, newData, 0, length);
+
+    // Copy data from other buffer
+    System.arraycopy(buf.data, buf.offset, newData, length, buf.length);
+
+    ByteBuffer newBuf = new ByteBuffer(newData);
+
+    // Copy our (older) metadata to new buffer, because handler might store some
+    // metadata in buffer, which is pushed back.
+    if (metadata != null)
+      newBuf.metadata = new HashMap<String, Object>(metadata);
+
+    return newBuf;
+  }
+
+  /**
+   * Copy used portion of buffer to new byte array. Expensive operation.
+   */
+  public byte[] toByteArray() {
+    return Arrays.copyOfRange(data, offset, offset + length);
+  }
+
+  public short[] toShortArray() {
+    if (length % 2 != 0)
+      throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 2 without remainder. Array length: " + length + ", remainder: "
+          + (length % 2) + ".");
+
+    short[] buf = new short[length / 2];
+
+    for (int i = 0, j = offset; i < buf.length; i++, j += 2) {
+      buf[i] = (short) ((data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8));
+    }
+    return buf;
+  }
+
+  /**
+   * Return array of int's in little endian order.
+   */
+  public int[] toIntLEArray() {
+    if (length % 4 != 0)
+      throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 4 without remainder. Array length: " + length + ", remainder: "
+          + (length % 4) + ".");
+
+    int[] buf = new int[length / 4];
+
+    for (int i = 0, j = offset; i < buf.length; i++, j += 4) {
+      buf[i] = (data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8) | ((data[j + 2] & 0xFF) << 16) | ((data[j + 3] & 0xFF) << 24);
+    }
+    return buf;
+  }
+
+  /**
+   * Return array of int's in little endian order, but use only 3 bytes per int (3RGB).
+   */
+  public int[] toInt3LEArray() {
+    if (length % 3 != 0)
+      throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 3 without remainder. Array length: " + length + ", remainder: "
+          + (length % 3) + ".");
+
+    int[] buf = new int[length / 3];
+
+    for (int i = 0, j = offset; i < buf.length; i++, j += 3) {
+      buf[i] = (data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8) | ((data[j + 2] & 0xFF) << 16);
+    }
+    return buf;
+  }
+
+  /**
+   * Helper method for test cases to convert array of byte arrays to array of
+   * byte buffers.
+   */
+  public static ByteBuffer[] convertByteArraysToByteBuffers(byte[]... bas) {
+    ByteBuffer bufs[] = new ByteBuffer[bas.length];
+
+    int i = 0;
+    for (byte[] ba : bas) {
+      bufs[i++] = new ByteBuffer(ba);
+    }
+    return bufs;
+  }
+
+  /**
+   * Read signed int in network order. Cursor is advanced by 4.
+   */
+  public int readSignedInt() {
+    if (cursor + 4 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
+
+    int result = (((data[offset + cursor] & 0xff) << 24) + ((data[offset + cursor + 1] & 0xff) << 16) + ((data[offset + cursor + 2] & 0xff) << 8) + (data[offset
+        + cursor + 3] & 0xff));
+    cursor += 4;
+    return result;
+  }
+
+  /**
+   * Read signed int in little endian order. Cursor is advanced by 4.
+   */
+  public int readSignedIntLE() {
+    if (cursor + 4 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
+
+    int result = (((data[offset + cursor + 3] & 0xff) << 24) + ((data[offset + cursor + 2] & 0xff) << 16) + ((data[offset + cursor + 1] & 0xff) << 8) + (data[offset
+        + cursor] & 0xff));
+    cursor += 4;
+    return result;
+  }
+
+  /**
+   * Read unsigned int in little endian order. Cursor is advanced by 4.
+   */
+  public long readUnsignedIntLE() {
+    if (cursor + 4 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + ".");
+
+    long result = (((long) (data[offset + cursor + 3] & 0xff) << 24) + ((long) (data[offset + cursor + 2] & 0xff) << 16)
+        + ((long) (data[offset + cursor + 1] & 0xff) << 8) + (long) (data[offset + cursor] & 0xff));
+    cursor += 4;
+    return result;
+  }
+
+  /**
+   * Read signed int in variable length format. Top most bit of each byte
+   * indicates that next byte contains additional bits. Cursor is advanced by
+   * 1-5 bytes.
+   */
+  public int readVariableSignedIntLE() {
+    int result = 0;
+
+    for (int shift = 0; shift < 32; shift += 7) {
+      int b = readUnsignedByte();
+      result |= (b & 0x7f) << shift;
+      if ((b & 0x80) == 0)
+        break;
+    }
+
+    return result;
+  }
+
+  /**
+   * Read unsigned int in network order in variable length format. Cursor is
+   * advanced by 1 to 4 bytes.
+   * 
+   * Two most significant bits of first byte indicates length of field: 0x00 - 1
+   * byte, 0x40 - 2 bytes, 0x80 - 3 bytes, 0xc0 - 4 bytes.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc241614.aspx
+   */
+  public int readEncodedUnsignedInt() {
+    int firstByte = readUnsignedByte();
+    int result;
+    switch (firstByte & 0xc0) {
+    default:
+    case 0x00:
+      result = firstByte & 0x3f;
+      break;
+    case 0x40:
+      result = (firstByte & 0x3f << 8) | readUnsignedByte();
+      break;
+    case 0x80:
+      result = (((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte();
+      break;
+    case 0xc0:
+      result = ((((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte() << 8) | readUnsignedByte();
+      break;
+    }
+
+    return result;
+  }
+
+  /**
+   * Read unsigned byte. Cursor is advanced by 1.
+   */
+  public int readUnsignedByte() {
+    if (cursor + 1 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 1 byte from this buffer: " + this + ".");
+
+    int b = data[offset + cursor] & 0xff;
+    cursor += 1;
+    return b;
+  }
+
+  /**
+   * Read signed byte. Cursor is advanced by 1.
+   */
+  public byte readSignedByte() {
+    if (cursor + 1 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 1 byte from this buffer: " + this + ".");
+
+    byte b = data[offset + cursor];
+    cursor += 1;
+    return b;
+  }
+
+  /**
+   * Read unsigned short in network order. Cursor is advanced by 2.
+   */
+  public int readUnsignedShort() {
+    if (cursor + 2 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
+
+    int result = (((data[offset + cursor] & 0xff) << 8) | (data[offset + cursor + 1] & 0xff));
+    cursor += 2;
+    return result;
+  }
+
+  /**
+   * Read signed short in little endian order. Cursor is advanced by 2.
+   */
+  public short readSignedShortLE() {
+    if (cursor + 2 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
+
+    short result = (short) (((data[offset + cursor + 1] & 0xff) << 8) | (data[offset + cursor] & 0xff));
+    cursor += 2;
+    return result;
+  }
+
+  /**
+   * Read unsigned short in network order in variable length format. Cursor is
+   * advanced by 1 or 2 bytes.
+   * 
+   * Most significant bit of first byte indicates length of field: 0 - 1 byte, 1
+   * - 2 bytes.
+   */
+  public int readVariableUnsignedShort() {
+    int firstByte = readUnsignedByte();
+
+    int result;
+    if ((firstByte & 0x80) == 0)
+      result = firstByte & 0x7f;
+    else {
+      int secondByte = readUnsignedByte();
+      result = (((firstByte & 0x7f) << 8) | secondByte);
+    }
+
+    return result;
+  }
+
+  /**
+   * Read unsigned short in little endian order. Cursor is advanced by 2.
+   */
+  public int readUnsignedShortLE() {
+    if (cursor + 2 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + ".");
+
+    int result = (((data[offset + cursor + 1] & 0xff) << 8) | (data[offset + cursor] & 0xff));
+    cursor += 2;
+    return result;
+  }
+
+  /**
+   * Read unsigned short in network order in variable length format. Cursor is
+   * advanced by 1 or 2 bytes.
+   * 
+   * Most significant bit of first byte indicates length of field: 0x00 - 1
+   * byte, 0x80 - 2 bytes.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc241612.aspx
+   */
+  public int readEncodedUnsignedShort() {
+    int firstByte = readUnsignedByte();
+
+    int result;
+    if ((firstByte & 0x80) == 0)
+      result = firstByte & 0x7f;
+    else {
+      int secondByte = readUnsignedByte();
+      result = (((firstByte & 0x7f) << 8) | secondByte);
+    }
+
+    return result;
+  }
+
+  /**
+   * Read signed short in network order in variable length format. Cursor is
+   * advanced by 1 or 2 bytes.
+   * 
+   * Most significant bit of first byte indicates length of field: 0x00 - 1
+   * byte, 0x80 - 2 bytes. Second most significant bit indicates is value
+   * positive or negative.
+   * 
+   * @see http://msdn.microsoft.com/en-us/library/cc241613.aspx
+   */
+  public int readEncodedSignedShort() {
+    int firstByte = readUnsignedByte();
+
+    int result;
+    if ((firstByte & 0x80) == 0)
+      result = firstByte & 0x3f;
+    else {
+      int secondByte = readUnsignedByte();
+      result = (((firstByte & 0x3f) << 8) | secondByte);
+    }
+
+    if ((firstByte & 0x40) > 0)
+      return -result;
+    else
+      return result;
+  }
+
+  /**
+   * Read signed long in little endian order. Cursor is advanced by 8 bytes.
+   */
+  public long readSignedLongLE() {
+    return (((long) readSignedIntLE()) & 0xffFFffFFL) | (((long) readSignedIntLE()) << 32);
+  }
+
+  /**
+   * Read string from buffer. Cursor is advanced by string length.
+   */
+  public String readString(int length, Charset charset) {
+    if (cursor + length > this.length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read " + length + " bytes from this buffer: " + this + ".");
+
+    String string = new String(data, offset + cursor, length, charset);
+    cursor += length;
+    return string;
+  }
+
+  /**
+   * Get bytes as lightweight slice. Cursor is advanced by data length.
+   */
+  public ByteBuffer readBytes(int dataLength) {
+    if (cursor + dataLength > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read " + dataLength + " bytes from this buffer: " + this + ".");
+
+    ByteBuffer slice = slice(cursor, dataLength, false);
+    cursor += dataLength;
+    return slice;
+  }
+
+  /**
+   * Cursor is advanced by given number of bytes.
+   */
+  public void skipBytes(int numOfBytes) {
+    if (cursor + numOfBytes > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot read " + numOfBytes + " bytes from this buffer: " + this + ".");
+
+    cursor += numOfBytes;
+  }
+
+  /**
+   * Write byte. Cursor is advanced by 1.
+   */
+  public void writeByte(int b) {
+    if (cursor + 1 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot write 1 byte to this buffer: " + this + ".");
+
+    data[offset + cursor] = (byte) b;
+    cursor += 1;
+  }
+
+  /**
+   * Write short in network order. Cursor is advanced by 2.
+   */
+  public void writeShort(int x) {
+    if (cursor + 2 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot write 2 bytes to this buffer: " + this + ".");
+
+    data[offset + cursor] = (byte) (x >> 8);
+    data[offset + cursor + 1] = (byte) x;
+    cursor += 2;
+  }
+
+  /**
+   * Write short in little endian order. Cursor is advanced by 2.
+   */
+  public void writeShortLE(int x) {
+    if (cursor + 2 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot write 2 bytes to this buffer: " + this + ".");
+
+    data[offset + cursor + 1] = (byte) (x >> 8);
+    data[offset + cursor] = (byte) x;
+    cursor += 2;
+  }
+
+  /**
+   * Write int in network order. Cursor is advanced by 4.
+   */
+  public void writeInt(int i) {
+    if (cursor + 4 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot write 4 bytes to this buffer: " + this + ".");
+
+    data[offset + cursor] = (byte) (i >> 24);
+    data[offset + cursor + 1] = (byte) (i >> 16);
+    data[offset + cursor + 2] = (byte) (i >> 8);
+    data[offset + cursor + 3] = (byte) i;
+    cursor += 4;
+  }
+
+  public void writeIntLE(int i) {
+    if (cursor + 4 > length)
+      throw new ArrayIndexOutOfBoundsException("Cannot write 4 bytes to this buffer: " + this + ".");
+
+    data[offset + cursor] = (byte) i;
+    data[offset + cursor + 1] = (byte) (i >> 8);
+    data[offset + cursor + 2] = (byte) (i >> 16);
+    data[offset + cursor + 3] = (byte) (i >> 24);
+    cursor += 4;
+  }
+
+  /**
+   * Write int in variable length format. Cursor is advanced by number of bytes
+   * written (1-5).
+   * 
+   * Topmost bit of each byte is set to 1 to indicate that next byte has data.
+   */
+  public void writeVariableIntLE(int i) {
+    while (i != 0) {
+      // Get lower bits of number
+      int b = i & 0x7f;
+      i >>= 7;
+
+      if (i > 0)
+        // Set topmost bit of byte to indicate that next byte(s) contains
+        // remainder bits
+        b |= 0x80;
+
+      writeByte(b);
+    }
+  }
+
+  /**
+   * Write short in variable length format. Cursor is advanced by number of
+   * bytes written (1-2).
+   * 
+   * Topmost bit of first byte is set to 1 to indicate that next byte has data.
+   */
+  public void writeVariableShort(int length) {
+    if (length > 0x7f | length < 0)
+      writeShort(length | 0x8000);
+    else
+      writeByte(length);
+  }
+
+  /**
+   * Prepend given data to this byte buffer.
+   */
+  public void prepend(ByteBuffer buf) {
+    prepend(buf.data, buf.offset, buf.length);
+  }
+
+  /**
+   * Prepend given data to this byte buffer.
+   */
+  public void prepend(byte[] data) {
+    prepend(data, 0, data.length);
+  }
+
+  /**
+   * Prepend given data to this byte buffer.
+   */
+  public void prepend(byte[] data, int offset, int length) {
+    if (!isSoleOwner()) {
+      throw new RuntimeException("Create full copy of this byte buffer data for modification. refCount: " + refCount + ", parentByteBuffer: "
+          + parentByteBuffer + ".");
+    }
+
+    // If there is no enough space for header to prepend
+    if (!(this.offset >= length)) {
+      throw new RuntimeException("Reserve data to have enough space for header.");
+    }
+
+    // Copy header
+    System.arraycopy(data, offset, this.data, this.offset - length, length);
+
+    // Extend byte range to include header
+    this.offset -= length;
+    this.length += length;
+    this.cursor += length;
+  }
+
+  public void writeString(String str, Charset charset) {
+    writeBytes(str.getBytes(charset));
+  }
+
+  /**
+   * Write string of fixed size. When string is shorted, empty space is filled
+   * with zeros. When string is larger, it is truncated.
+   */
+  public void writeFixedString(int length, String str, Charset charset) {
+    byte[] bytes = str.getBytes(charset);
+    writeBytes(bytes, 0, Math.min(bytes.length, length));
+
+    for (int i = bytes.length; i < length; i++)
+      writeByte(0);
+  }
+
+  public void writeBytes(ByteBuffer buf) {
+    writeBytes(buf.data, buf.offset, buf.length);
+  }
+
+  public void writeBytes(byte[] bytes) {
+    writeBytes(bytes, 0, bytes.length);
+  }
+
+  public void writeBytes(byte[] bytes, int offset, int length) {
+    System.arraycopy(bytes, offset, this.data, this.offset + this.cursor, length);
+    cursor += length;
+  }
+
+  // /**
+  // * Write BER encoded definite long variant of the ASN.1 length field.
+  // */
+  // public void writeBerLength(int value) {
+  // int fieldLength;
+  // if (value > 0xFFffFF)
+  // fieldLength = 4;
+  // else if (value > 0xFFff)
+  // fieldLength = 3;
+  // else if (value > 0xFF)
+  // fieldLength = 2;
+  // else
+  // fieldLength = 1;
+  //
+  // if (cursor + fieldLength + 1 > length)
+  // throw new ArrayIndexOutOfBoundsException("Cannot write " + (fieldLength +
+  // 1) + " byte(s) to this buffer: " + this + ".");
+  //
+  // // Write length of length field itself
+  // writeByte(0x80 | fieldLength);
+  //
+  // switch (fieldLength) {
+  // case 4:
+  // data[offset + cursor++] = (byte) (value >> 24);
+  // case 3:
+  // data[offset + cursor++] = (byte) (value >> 16);
+  // case 2:
+  // data[offset + cursor++] = (byte) (value >> 8);
+  // case 1:
+  // data[offset + cursor++] = (byte) value;
+  // }
+  //
+  // }
+
+  /**
+   * Reduce length of buffer to cursor position.
+   */
+  public void trimAtCursor() {
+    length = cursor;
+  }
+
+  /**
+   * Rewind cursor to beginning of buffer.
+   */
+  public void rewindCursor() {
+    cursor = 0;
+  }
+
+  /**
+   * Read RGB color in LE order. Cursor is advanced by 3.
+   * 
+   * @return color as int, with red in lowest octet.
+   */
+  public int readRGBColor() {
+    return readUnsignedByte() | (readUnsignedByte() << 8) | (readUnsignedByte() << 16);
+  }
+
+  public void assertThatBufferIsFullyRead() {
+    if (cursor != length)
+      throw new RuntimeException("Data in buffer is not read fully. Buf: " + this + ".");
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+
+    int end = offset + length;
+    for (int i = offset; i < end; i++)
+      result = 31 * result + data[i];
+
+    result = prime * result + length;
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (obj == null)
+      return false;
+    if (getClass() != obj.getClass())
+      return false;
+
+    ByteBuffer other = (ByteBuffer) obj;
+    if (length != other.length)
+      return false;
+
+    for (int i = 0; i < length; i++)
+      if (data[offset + i] != other.data[other.offset + i])
+        return false;
+
+    return true;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java
new file mode 100644
index 0000000..e3de289
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java
@@ -0,0 +1,24 @@
+// 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 streamer;
+
+public interface DataSink {
+
+  void sendData(ByteBuffer buf);
+
+  void sendEvent(Event event, Direction direction);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java
new file mode 100644
index 0000000..152be2e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java
@@ -0,0 +1,60 @@
+// 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 streamer;
+
+public interface DataSource {
+
+  /**
+   * Get data from source.
+   * 
+   * @param block
+   *          if false, then return immediately when no data is available,
+   *          otherwise wait for data
+   * @return new data or null, when no data is available
+   */
+  ByteBuffer pull(boolean block);
+
+  /**
+   * Hold data temporary to use at next pull or push.
+   * 
+   * @param buf
+   *          a data
+   */
+  void pushBack(ByteBuffer buf);
+
+  /**
+   * Hold data temporary to use at next pull. Don't return abything untill given
+   * amount of data will be read from source, because data will be pushed back
+   * anyway.
+   * 
+   * @param buf
+   *          a data
+   * @param lengthOfFullPacket
+   *          length of full block of data to read from source
+   */
+  void pushBack(ByteBuffer buf, int lengthOfFullPacket);
+
+  /**
+   * Send event to pads.
+   * 
+   * @param event
+   *          a event
+   * @param direction
+   *          pad direction
+   */
+  void sendEvent(Event event, Direction direction);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.java
new file mode 100644
index 0000000..c9dede8
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.java
@@ -0,0 +1,21 @@
+// 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 streamer;
+
+public enum Direction {
+  IN, OUT
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java
new file mode 100644
index 0000000..c927dea
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java
@@ -0,0 +1,120 @@
+// 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 streamer;
+
+import java.util.Set;
+
+/**
+ * Element is for processing of data. It has one or more contact pads, which can
+ * be wired with other elements using links.
+ */
+public interface Element {
+
+  /**
+   * Name of pad for standard input. Should be set in all elements except pure
+   * sinks.
+   */
+  public static final String STDIN = "stdin";
+
+  /**
+   * Name of pad for standard output. Should be set in all elements except pure
+   * sources.
+   */
+  public static final String STDOUT = "stdout";
+
+  /**
+   * Get link connected to given pad.
+   * 
+   * @param padName
+   *          Standard pads are "stdin" and "stdout".
+   */
+  Link getLink(String padName);
+
+  /**
+   * Get pads of this element.
+   */
+  Set<String> getPads(Direction direction);
+
+  /**
+   * Connect link to given pad.
+   * 
+   * @param padName
+   *          a pad name. Standard pads are "stdin" and "stdout".
+   */
+  void setLink(String padName, Link link, Direction direction);
+
+  /**
+   * Disconnect link from given pad.
+   * 
+   * @param padName
+   *          Standard pads are "stdin" and "stdout".
+   */
+  void dropLink(String padName);
+
+  /**
+   * Pull data from element and handle it. Element should ask one of it input
+   * pads for data, handle data and push result to it sink(s), if any.
+   * 
+   * @param block
+   *          block until data will be available, or do a slight delay at least,
+   *          when data is not available
+   */
+  void poll(boolean block);
+
+  /**
+   * Handle incoming data.
+   * 
+   * @param buf
+   *          a data
+   * @param link
+   *          TODO
+   */
+  void handleData(ByteBuffer buf, Link link);
+
+  /**
+   * Handle event.
+   * 
+   * @param event
+   *          an event
+   * @param direction
+   *          if IN, then send event to input pads, when OUT, then send to
+   *          output pads
+   */
+  void handleEvent(Event event, Direction direction);
+
+  /**
+   * Get element ID.
+   */
+  String getId();
+
+  /**
+   * Validate element: check is all required pads are connected.
+   */
+  void validate();
+
+  /**
+   * Drop link.
+   * 
+   * @param link a link to drop
+   */
+  void dropLink(Link link);
+
+  /**
+   * Drop existing link and replace it by new link.
+   */
+  void replaceLink(Link existingLink, Link newLink);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java
new file mode 100644
index 0000000..5e1a389
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java
@@ -0,0 +1,33 @@
+// 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 streamer;
+
+public enum Event {
+  STREAM_START, 
+  STREAM_CLOSE,
+  
+  /**
+   * Upgrade socket to SSL.
+   */
+  SOCKET_UPGRADE_TO_SSL,
+  
+  /**
+   * Switch links to input mode.
+   */
+  LINK_SWITCH_TO_PULL_MODE
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java
new file mode 100644
index 0000000..65fb29e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java
@@ -0,0 +1,69 @@
+// 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 streamer;
+
+public class FakeSink extends BaseElement {
+
+  public FakeSink(String id) {
+    super(id);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Received buf #" + (packetNumber) + " " + buf + ".");
+
+    if (buf == null)
+      return;
+
+    // Use packetNumber variable to count incoming packets
+    packetNumber++;
+
+    buf.unref();
+  }
+
+  @Override
+  public String toString() {
+    return "FakeSink(" + id + ")";
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Event received: " + event + ".");
+
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+
+    Element sink = new FakeSink("sink") {
+      {
+        verbose = true;
+      }
+    };
+
+    byte[] data = new byte[] { 1, 2, 3 };
+    ByteBuffer buf = new ByteBuffer(data);
+    sink.setLink(STDIN, new SyncLink(), Direction.IN);
+    sink.getLink(STDIN).sendData(buf);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java
new file mode 100644
index 0000000..4cf6503
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java
@@ -0,0 +1,125 @@
+// 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 streamer;
+
+public class FakeSource extends BaseElement {
+
+  /**
+   * Delay for null packets in poll method when blocking is requested, in
+   * milliseconds.
+   */
+  protected long delay = SyncLink.STANDARD_DELAY_FOR_EMPTY_PACKET;
+
+  public FakeSource(String id) {
+    super(id);
+  }
+
+  @Override
+  public void poll(boolean block) {
+    if (numBuffers > 0 && packetNumber >= numBuffers) {
+      // Close stream when limit of packets is reached
+      sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
+      return;
+    }
+
+    // Prepare new packet
+    ByteBuffer buf = initializeData();
+
+    // Push it to output(s)
+    pushDataToAllOuts(buf);
+
+    // Make slight delay when blocking input was requested (to avoid
+    // consuming of 100% in parent loop)
+    if (block)
+      delay();
+
+  }
+
+  /**
+   * Make slight delay. Should be used when blocking input is requested in pull
+   * mode, but null packed was returned by input.
+   */
+  protected void delay() {
+    try {
+      Thread.sleep(delay);
+    } catch (InterruptedException e) {
+    }
+  }
+
+  /**
+   * Initialize data.
+   */
+  public ByteBuffer initializeData() {
+    ByteBuffer buf = new ByteBuffer(incommingBufLength);
+
+    // Set first byte of package to it sequance number
+    buf.data[buf.offset] = (byte) (packetNumber % 128);
+
+    // Initialize rest of bytes with sequential values, which are
+    // corresponding with their position in byte buffer
+    for (int i = buf.offset + 1; i < buf.length; i++)
+      buf.data[i] = (byte) (i % 128);
+
+    buf.putMetadata(ByteBuffer.SEQUENCE_NUMBER, packetNumber);
+    buf.putMetadata("src", id);
+
+    return buf;
+  }
+
+  @Override
+  public String toString() {
+    return "FakeSource(" + id + ")";
+  }
+
+  public static void main(String args[]) {
+
+    Element fakeSource = new FakeSource("source 3/10/100") {
+      {
+        verbose = true;
+        this.incommingBufLength = 3;
+        this.numBuffers = 10;
+        this.delay = 100;
+      }
+    };
+
+    Element fakeSink = new FakeSink("sink") {
+      {
+        this.verbose = true;
+      }
+    };
+
+    Element fakeSink2 = new FakeSink("sink2") {
+      {
+        this.verbose = true;
+      }
+    };
+
+    Link link = new SyncLink();
+
+    fakeSource.setLink(STDOUT, link, Direction.OUT);
+    fakeSink.setLink(STDIN, link, Direction.IN);
+
+    Link link2 = new SyncLink();
+
+    fakeSource.setLink("out2", link2, Direction.OUT);
+    fakeSink2.setLink(STDIN, link2, Direction.IN);
+
+    link.run();
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java
new file mode 100644
index 0000000..b05637f
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java
@@ -0,0 +1,194 @@
+// 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 streamer;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Source element, which reads data from InputStream.
+ */
+public class InputStreamSource extends BaseElement {
+
+  protected InputStream is;
+  protected SocketWrapper socketWrapper;
+
+  public InputStreamSource(String id) {
+    super(id);
+  }
+
+  public InputStreamSource(String id, InputStream is) {
+    super(id);
+    this.is = is;
+  }
+
+  public InputStreamSource(String id, SocketWrapper socketWrapper) {
+    super(id);
+    this.socketWrapper = socketWrapper;
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    switch (event) {
+    case SOCKET_UPGRADE_TO_SSL:
+      socketWrapper.upgradeToSsl();
+      break;
+    default:
+      super.handleEvent(event, direction);
+    }
+  }
+
+  @Override
+  public void setLink(String padName, Link link, Direction direction) {
+    switch (direction) {
+    case OUT:
+      super.setLink(padName, link, direction);
+
+      if (is == null) {
+        // Pause links until data stream will be ready
+        link.pause();
+      }
+      break;
+    case IN:
+      throw new RuntimeException("Cannot assign link to input pad in source element. Element: " + this + ", pad: " + padName + ", link: " + link + ".");
+    }
+  }
+
+  public void setInputStream(InputStream is) {
+    this.is = is;
+
+    // Resume links
+    resumeLinks();
+  }
+
+  private void resumeLinks() {
+    for (DataSink sink : outputPads.values())
+      ((Link) sink).resume();
+  }
+
+  /**
+   * Read data from input stream.
+   */
+  @Override
+  public void poll(boolean block) {
+    try {
+      if (!block && is.available() == 0) {
+
+        if (verbose)
+          System.out.println("[" + this + "] INFO: No data in stream is available now, returning.");
+
+        return;
+      }
+
+      // Create buffer of recommended size and with default offset
+      ByteBuffer buf = new ByteBuffer(incommingBufLength);
+
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Reading data from stream.");
+
+      int actualLength = is.read(buf.data, buf.offset, buf.data.length - buf.offset);
+
+      if (actualLength < 0) {
+        if (verbose)
+          System.out.println("[" + this + "] INFO: End of stream.");
+
+        buf.unref();
+        closeStream();
+        sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
+        return;
+      }
+
+      if (actualLength == 0) {
+        if (verbose)
+          System.out.println("[" + this + "] INFO: Empty buffer is read from stream.");
+
+        buf.unref();
+        return;
+      }
+
+      buf.length = actualLength;
+
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Data read from stream: " + buf + ".");
+
+      pushDataToAllOuts(buf);
+
+    } catch (IOException e) {
+      System.err.println("[" + this + "] ERROR: " + e.getMessage());
+      closeStream();
+    }
+  }
+
+  @Override
+  protected void onClose() {
+    closeStream();
+  }
+
+  private void closeStream() {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Closing stream.");
+
+    try {
+      is.close();
+    } catch (IOException e) {
+    }
+    try {
+      sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
+    } catch (Exception e) {
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "InputStreamSource(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    InputStream is = new ByteArrayInputStream(new byte[] { 1, 2, 3 });
+
+    InputStreamSource source = new InputStreamSource("source") {
+      {
+        verbose = true;
+      }
+    };
+    Element fakeSink = new FakeSink("sink") {
+      {
+        verbose = true;
+      }
+    };
+
+    Link link = new SyncLink() {
+      {
+        verbose = true;
+      }
+    };
+
+    source.setLink(STDOUT, link, Direction.OUT);
+    fakeSink.setLink(STDIN, link, Direction.IN);
+
+    source.setInputStream(is);
+
+    link.sendEvent(Event.STREAM_START, Direction.OUT);
+    link.run();
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java
new file mode 100644
index 0000000..bd970f0
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java
@@ -0,0 +1,66 @@
+// 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 streamer;
+
+/**
+ * Link is wire between two elements. It always must contain source and sink
+ * elements.
+ */
+public interface Link extends DataSource, DataSink, Runnable {
+
+  /**
+   * Wire this link with given sink.
+   * 
+   * @param sink
+   *          an Element
+   * @return same sink element, for chaining
+   */
+  Element setSink(Element sink);
+
+  /**
+   * Wire this link with given source.
+   * 
+   * @param source
+   *          an Element
+   * @return same source element, for chaining
+   */
+  Element setSource(Element source);
+
+  Element getSource();
+
+  Element getSink();
+
+  /**
+   * Hold all data in cache, don't pass data to sink until resumed.
+   */
+  void pause();
+
+  /**
+   * Resume transfer.
+   */
+  void resume();
+  
+  /**
+   * Change mode of operation of this link from push mode to pull mode.
+   */
+  void setPullMode();
+  
+  /**
+   * Drop this link.
+   */
+  void drop();
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java
new file mode 100644
index 0000000..ce9fdf9
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java
@@ -0,0 +1,111 @@
+// 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 streamer;
+
+import java.util.Arrays;
+
+/**
+ * Compare incoming packets with expected packets.
+ */
+public class MockSink extends BaseElement {
+
+  protected ByteBuffer bufs[] = null;
+
+  public MockSink(String id) {
+    super(id);
+  }
+
+  public MockSink(String id, ByteBuffer bufs[]) {
+    super(id);
+    this.bufs = bufs;
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Received buf #" + (packetNumber) + " " + buf + ".");
+
+    if (buf == null)
+      return;
+
+    if (packetNumber >= bufs.length)
+      throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not expected. Number of expected buffers: " + bufs.length
+          + ", unexpected buffer: " + buf + ".");
+
+    // Compare incoming buffer with expected buffer
+    if (!Arrays.equals(bufs[packetNumber].toByteArray(), buf.toByteArray()))
+      throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not equal to expected buffer.\n  Actual bufer: " + buf
+          + ",\n  expected buffer: " + bufs[packetNumber] + ".");
+
+    if (verbose)
+      System.out.println("[" + this + "] INFO: buffers are equal.");
+
+    // Use packetNumber variable to count incoming packets
+    packetNumber++;
+
+    buf.unref();
+  }
+
+  @Override
+  protected void onClose() {
+    super.onClose();
+
+    if (packetNumber != bufs.length)
+      throw new AssertionError("[" + this + "] Number of expected buffers: " + bufs.length + ", number of actual buffers: " + packetNumber + ".");
+  }
+
+  @Override
+  public String toString() {
+    return "MockSink(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+
+    Element mockSource = new MockSource("source") {
+      {
+        this.bufs = new ByteBuffer[] { new ByteBuffer(new byte[] { 1, 1, 2, 3, 4, 5 }), new ByteBuffer(new byte[] { 2, 1, 2, 3, 4 }),
+            new ByteBuffer(new byte[] { 3, 1, 2, 3 }), new ByteBuffer(new byte[] { 4, 1, 2 }), new ByteBuffer(new byte[] { 5, 1 }) };
+        this.verbose = true;
+        this.delay = 100;
+        this.numBuffers = this.bufs.length;
+      }
+    };
+
+    Element mockSink = new MockSink("sink") {
+      {
+        this.bufs = new ByteBuffer[] { new ByteBuffer(new byte[] { 1, 1, 2, 3, 4, 5 }), new ByteBuffer(new byte[] { 2, 1, 2, 3, 4 }),
+            new ByteBuffer(new byte[] { 3, 1, 2, 3 }), new ByteBuffer(new byte[] { 4, 1, 2 }), new ByteBuffer(new byte[] { 5, 1 }) };
+        this.verbose = true;
+      }
+    };
+
+    Link link = new SyncLink() {
+      {
+        this.verbose = true;
+      }
+    };
+
+    mockSource.setLink(STDOUT, link, Direction.OUT);
+    mockSink.setLink(STDIN, link, Direction.IN);
+
+    link.run();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java
new file mode 100644
index 0000000..db47db2
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java
@@ -0,0 +1,88 @@
+// 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 streamer;
+
+public class MockSource extends FakeSource {
+
+  protected ByteBuffer bufs[] = null;
+
+  public MockSource(String id) {
+    super(id);
+  }
+
+  public MockSource(String id, ByteBuffer bufs[]) {
+    super(id);
+    this.bufs = bufs;
+  }
+
+  /**
+   * Initialize data.
+   */
+  @Override
+  public ByteBuffer initializeData() {
+    if (packetNumber >= bufs.length) {
+      sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
+      return null;
+    }
+
+    ByteBuffer buf = bufs[packetNumber];
+
+    buf.putMetadata(ByteBuffer.SEQUENCE_NUMBER, packetNumber);
+    return buf;
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Event received: " + event + ".");
+
+  }
+
+  @Override
+  public String toString() {
+    return "MockSource(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+
+    Element mockSource = new MockSource("source") {
+      {
+        this.bufs = new ByteBuffer[] { new ByteBuffer(new byte[] { 1, 1, 2, 3, 4, 5 }), new ByteBuffer(new byte[] { 2, 1, 2, 3, 4 }),
+            new ByteBuffer(new byte[] { 3, 1, 2, 3 }), new ByteBuffer(new byte[] { 4, 1, 2 }), new ByteBuffer(new byte[] { 5, 1 }) };
+        this.verbose = true;
+        this.delay = 100;
+        // this.numBuffers = this.bufs.length;
+      }
+    };
+
+    Element fakeSink = new FakeSink("sink") {
+      {
+        this.verbose = true;
+      }
+    };
+
+    Link link = new SyncLink();
+
+    mockSource.setLink(STDOUT, link, Direction.OUT);
+    fakeSink.setLink(STDIN, link, Direction.IN);
+
+    link.run();
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java
new file mode 100644
index 0000000..a7d4848
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java
@@ -0,0 +1,133 @@
+// 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 streamer;
+
+/**
+ * One time switch for handshake and initialization stages.
+ * 
+ * At beginning, element handles data internally, sending output to "otout" pad.
+ * After switchOff() method is called, element drops its links, so packets from
+ * "stdin" pad are forwarded directly to "stdout" pad, without processing.
+ * 
+ * Event STREAM_START is captured by this element and not propagated further.
+ * When switchOff() method is called, event STREAM_START is generated and sent
+ * to "stdout".
+ */
+public abstract class OneTimeSwitch extends BaseElement {
+
+  /**
+   * One-time out - name of output pad for one time logic. By default, output
+   * directly to socket.
+   */
+  public static final String OTOUT = "otout";
+
+  private boolean switched = false;
+
+  public OneTimeSwitch(String id) {
+    super(id);
+    declarePads();
+  }
+
+  protected void declarePads() {
+    inputPads.put(STDIN, null);
+    outputPads.put(OTOUT, null);
+    outputPads.put(STDOUT, null);
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (switched)
+      throw new RuntimeException(this + " element is switched off and must not receive any data or events anymore.");
+
+    if (buf == null)
+      return;
+
+    handleOneTimeData(buf, link);
+  }
+
+  public void pushDataToOTOut(ByteBuffer buf) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Sending data:  " + buf + ".");
+
+    outputPads.get(OTOUT).sendData(buf);
+  }
+
+  /**
+   * Switch this element off. Pass data directly to main output(s).
+   */
+  public void switchOff() {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Switching OFF.");
+
+    switched = true;
+    verbose = false;
+
+    // Rewire links: drop otout link, replace stdout link by stdin to send data
+    // directly to stdout
+    Link stdout = (Link) outputPads.get(STDOUT);
+    Link stdin = (Link) inputPads.get(STDIN);
+    Link otout = (Link) outputPads.get(OTOUT);
+
+    otout.drop();
+
+    // Wake up next peer(s)
+    sendEventToAllPads(Event.STREAM_START, Direction.OUT);
+
+    stdin.setSink(null);
+    inputPads.remove(STDIN);
+
+    Element nextPeer = stdout.getSink();
+    nextPeer.replaceLink(stdout, stdin);
+    stdout.drop();
+
+    for (Object link : inputPads.values().toArray())
+      ((Link) link).drop();
+    for (Object link : outputPads.values().toArray())
+      ((Link) link).drop();
+
+  }
+
+  public void switchOn() {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Switching ON.");
+
+    switched = false;
+  }
+
+  /**
+   * Override this method to handle one-time packet(s) at handshake or
+   * initialization stages. Execute method @see switchRoute() when this method
+   * is no longer necessary.
+   */
+  protected abstract void handleOneTimeData(ByteBuffer buf, Link link);
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    if (event == Event.STREAM_START) {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Event " + event + " is received.");
+
+      switchOn();
+
+      // Execute this element onStart(), but do not propagate event further,
+      // to not wake up next elements too early
+      onStart();
+    } else
+      super.handleEvent(event, direction);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.java
new file mode 100644
index 0000000..1d63850
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.java
@@ -0,0 +1,23 @@
+// 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 streamer;
+
+public class Order {
+
+  public Object type;
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java
new file mode 100644
index 0000000..d1aa5ce
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java
@@ -0,0 +1,153 @@
+// 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 streamer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class OutputStreamSink extends BaseElement {
+
+  protected OutputStream os;
+  protected SocketWrapper socketWrapper;
+
+  public OutputStreamSink(String id) {
+    super(id);
+  }
+
+  public OutputStreamSink(String id, OutputStream os) {
+    super(id);
+    this.os = os;
+  }
+
+  public OutputStreamSink(String id, SocketWrapper socketWrapper) {
+    super(id);
+    this.socketWrapper = socketWrapper;
+  }
+
+  public void setOutputStream(OutputStream os) {
+    this.os = os;
+    // Resume links
+    resumeLinks();
+  }
+
+  /**
+   * Send incoming data to stream.
+   */
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (buf == null)
+      return;
+
+    try {
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Writing data to stream: " + buf + ".");
+
+      os.write(buf.data, buf.offset, buf.length);
+      os.flush();
+    } catch (IOException e) {
+      System.err.println("[" + this + "] ERROR: " + e.getMessage());
+      closeStream();
+    }
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    switch (event) {
+    case SOCKET_UPGRADE_TO_SSL:
+      socketWrapper.upgradeToSsl();
+      break;
+    default:
+      super.handleEvent(event, direction);
+    }
+  }
+
+  @Override
+  public void setLink(String padName, Link link, Direction direction) {
+    switch (direction) {
+    case IN:
+      super.setLink(padName, link, direction);
+
+      if (os == null)
+        // Pause links until data stream will be ready
+        link.pause();
+      break;
+    case OUT:
+      throw new RuntimeException("Cannot assign link to output pad in sink element. Element: " + this + ", pad: " + padName + ", link: " + link + ".");
+    }
+  }
+
+  private void resumeLinks() {
+    for (DataSource source : inputPads.values())
+      ((Link) source).resume();
+  }
+
+  @Override
+  protected void onClose() {
+    closeStream();
+  }
+
+  private void closeStream() {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Closing stream.");
+
+    try {
+      os.close();
+    } catch (IOException e) {
+    }
+    try {
+      sendEventToAllPads(Event.STREAM_CLOSE, Direction.IN);
+    } catch (Exception e) {
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "OutputStreamSink(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    Element source = new FakeSource("source") {
+      {
+        this.verbose = true;
+        this.numBuffers = 3;
+        this.incommingBufLength = 5;
+        this.delay = 100;
+      }
+    };
+
+    OutputStreamSink sink = new OutputStreamSink("sink") {
+      {
+        verbose = true;
+      }
+    };
+
+    Link link = new SyncLink();
+
+    source.setLink(STDOUT, link, Direction.OUT);
+    sink.setLink(STDIN, link, Direction.IN);
+
+    sink.setOutputStream(new ByteArrayOutputStream());
+
+    link.sendEvent(Event.STREAM_START, Direction.IN);
+    link.run();
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java
new file mode 100644
index 0000000..c369350
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java
@@ -0,0 +1,91 @@
+// 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 streamer;
+
+/**
+ * Pipeline groups multiple elements.
+ */
+public interface Pipeline extends Element {
+
+  static final String IN = Direction.IN.toString();
+  static final String OUT = Direction.OUT.toString();
+
+  /**
+   * Add elements to pipeline.
+   * 
+   * @param elements
+   */
+  void add(Element... elements);
+
+  /**
+   * Add elements to pipeline and link them in given order.
+   * 
+   * @param elements
+   */
+  void addAndLink(Element... elements);
+
+  /**
+   * Link elements in given order using SyncLink. Element name can have prefix
+   * "PADNAME< " or/and suffix " >PADNAME" to use given named pads instead of
+   * "stdin" and "stdout". I.e. <code>link("foo", "bar", "baz");</code> is equal
+   * to <code>link("foo >stdin", "stdout< bar >stdin", "stdout< baz");</code> .
+   * 
+   * Special elements "IN" and "OUT" are pointing to pipeline outer interfaces,
+   * so when pipeline will be connected with other elements, outside of this
+   * pipeline, they will be connected to IN and OUT elements.
+   * 
+   * Example:
+   * 
+   * <pre>
+   * pipeline.link(&quot;IN&quot;, &quot;foo&quot;, &quot;bar&quot;, &quot;OUT&quot;);
+   * // Make additional branch from foo to baz, and then to OUT
+   * pipeline.link(&quot;foo &gt;baz_out&quot;, &quot;baz&quot;, &quot;baz_in&lt; OUT&quot;);
+   * </pre>
+   * 
+   * @param elements
+   *          elements to link
+   */
+  void link(String... elements);
+
+  /**
+   * Get element by name.
+   * 
+   * @return an element
+   */
+  Element get(String elementName);
+
+  /**
+   * Get link by element name and pad name.
+   */
+  Link getLink(String elementName, String padName);
+
+  /**
+   * Set link by element name and pad name. Allows to link external elements
+   * into internal elements of pipeline. Special elements "IN" and "OUT" are
+   * pointing to pipeline outer interfaces.
+   */
+  void setLink(String elementName, String padName, Link link, Direction direction);
+
+  /**
+   * Get link connected to given pad in given element and run it main loop.
+   * @param separateThread
+   *          set to true to start main loop in separate thread.
+   * @param waitForStartEvent TODO
+   */
+  void runMainLoop(String element, String padName, boolean separateThread, boolean waitForStartEvent);
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java
new file mode 100644
index 0000000..abf132f
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java
@@ -0,0 +1,309 @@
+// 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 streamer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class PipelineImpl implements Pipeline {
+
+  protected String id;
+  protected boolean verbose = System.getProperty("streamer.Pipeline.debug", "false").equals("true");
+
+  public PipelineImpl(String id) {
+    this.id = id;
+    elements = initElementMap(id);
+  }
+
+  protected Map<String, Element> elements;
+
+  protected HashMap<String, Element> initElementMap(String id) {
+    HashMap<String, Element> map = new HashMap<String, Element>();
+
+    map.put(IN, new BaseElement(id + "." + IN));
+    map.put(OUT, new BaseElement(id + "." + OUT));
+    return map;
+  }
+
+  @Override
+  public Link getLink(String padName) {
+    Link link = elements.get(IN).getLink(padName);
+    if (link == null)
+      link = elements.get(OUT).getLink(padName);
+    return link;
+  }
+
+  @Override
+  public Set<String> getPads(Direction direction) {
+    switch (direction) {
+    case IN:
+      return elements.get(IN).getPads(direction);
+
+    case OUT:
+      return elements.get(OUT).getPads(direction);
+    }
+    return null;
+  }
+
+  @Override
+  public void validate() {
+    for (Element element : elements.values())
+      element.validate();
+
+    // Check IN element
+    {
+      Element element = get(IN);
+      int outPadsNumber = element.getPads(Direction.OUT).size();
+      int inPadsNumber = element.getPads(Direction.IN).size();
+      if ((outPadsNumber | inPadsNumber) > 0 && (outPadsNumber == 0 || inPadsNumber == 0))
+        throw new RuntimeException("[ " + this + "] Pads of input element of pipeline are not balanced. Element: " + element + ", output pads: "
+            + element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + ".");
+    }
+
+    // Check OUT element
+    {
+      Element element = get(OUT);
+      int outPadsNumber = element.getPads(Direction.OUT).size();
+      int inPadsNumber = element.getPads(Direction.IN).size();
+      if ((outPadsNumber | inPadsNumber) > 0 && (outPadsNumber == 0 || inPadsNumber == 0))
+        throw new RuntimeException("[ " + this + "] Pads of output element of pipeline are not balanced. Element: " + element + ", output pads: "
+            + element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + ".");
+    }
+
+  }
+
+  @Override
+  public void dropLink(String padName) {
+    if (elements.get(IN).getLink(padName) != null)
+      elements.get(IN).dropLink(padName);
+
+    if (elements.get(OUT).getLink(padName) != null)
+      elements.get(OUT).dropLink(padName);
+  }
+
+  @Override
+  public void dropLink(Link link) {
+    elements.get(IN).dropLink(link);
+    elements.get(OUT).dropLink(link);
+  }
+
+  @Override
+  public void replaceLink(Link existingLink, Link newLink) {
+    elements.get(IN).replaceLink(existingLink, newLink);
+    elements.get(OUT).replaceLink(existingLink, newLink);
+  }
+
+  @Override
+  public void setLink(String padName, Link link, Direction direction) {
+    // Wire links to internal elements instead
+    elements.get(direction.toString()).setLink(padName, link, direction);
+  }
+
+  @Override
+  public void poll(boolean block) {
+    throw new RuntimeException("Not implemented.");
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    get(IN).handleData(buf, link);
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    switch (direction) {
+    case IN:
+      get(IN).handleEvent(event, direction);
+      break;
+    case OUT:
+      get(OUT).handleEvent(event, direction);
+      break;
+    }
+  }
+
+  @Override
+  public void add(Element... elements) {
+    for (Element element : elements) {
+      String id = element.getId();
+
+      if (this.elements.containsKey(id))
+        throw new RuntimeException("This pipeline already contains element with same ID. New element: " + element + ", existing element: "
+            + this.elements.get(id) + ".");
+
+      this.elements.put(id, element);
+    }
+  }
+  
+  @Override
+  public void link(String... elementNames) {
+
+    if (elementNames.length < 2)
+      throw new RuntimeException("At least two elements are necessary to create link between them.");
+
+    // Parse array of element and pad names
+
+    Element elements[] = new Element[elementNames.length];
+    String inputPads[] = new String[elementNames.length];
+    String outputPads[] = new String[elementNames.length];
+
+    int i = 0;
+    for (String elementName : elementNames) {
+      if (elementName.contains("< ")) {
+        inputPads[i] = elementName.substring(0, elementName.indexOf("< "));
+        elementName = elementName.substring(elementName.indexOf("< ") + 2);
+      } else {
+        inputPads[i] = STDIN;
+      }
+
+      if (elementName.contains(" >")) {
+        outputPads[i] = elementName.substring(elementName.indexOf(" >") + 2);
+        elementName = elementName.substring(0, elementName.indexOf(" >"));
+      } else {
+        outputPads[i] = STDOUT;
+      }
+
+      elements[i] = get(elementName);
+
+      if (elements[i] == null)
+        throw new RuntimeException("Cannot find element by name in this pipeline. Element name: \"" + elementName + "\" (" + elementNames[i] + "), pipeline: "
+            + this + ".");
+
+      i++;
+    }
+
+    // Link elements
+    for (i = 0; i < elements.length - 1; i++) {
+      Element leftElement = elements[i];
+      Element rightElement = elements[i + 1];
+      String leftPad = outputPads[i];
+      String rightPad = inputPads[i + 1];
+
+      String linkId = leftElement.getId() + " >" + leftPad + " | " + rightPad + "< " + rightElement.getId();
+
+      if (verbose)
+        System.out.println("[" + this + "] INFO: Linking: " + linkId + ".");
+
+      Link link = new SyncLink(linkId);
+      leftElement.setLink(leftPad, link, Direction.OUT);
+      rightElement.setLink(rightPad, link, Direction.IN);
+    }
+  }
+
+  @Override
+  public void addAndLink(Element... elements) {
+    add(elements);
+    link(elements);
+  }
+
+  private void link(Element... elements) {
+    String elementNames[] = new String[elements.length];
+
+    int i = 0;
+    for (Element element : elements) {
+      elementNames[i++] = element.getId();
+    }
+
+    link(elementNames);
+  }
+
+  @Override
+  public Element get(String elementName) {
+    return elements.get(elementName);
+  }
+
+  @Override
+  public Link getLink(String elementName, String padName) {
+    return elements.get(elementName).getLink(padName);
+
+  }
+
+  @Override
+  public void setLink(String elementName, String padName, Link link, Direction direction) {
+    elements.get(elementName).setLink(padName, link, direction);
+  }
+
+  @Override
+  public String getId() {
+    return id;
+  }
+
+  @Override
+  public void runMainLoop(String elementName, String padName, boolean separateThread, boolean waitForStartEvent) {
+    validate();
+
+    Link link = getLink(elementName, padName);
+
+    if (link == null)
+      throw new NullPointerException("Cannot find link. Element name: " + elementName + ", element: " + get(elementName) + ", pad: " + padName + ".");
+
+    if (!waitForStartEvent)
+      link.sendEvent(Event.STREAM_START, Direction.OUT);
+
+    if (separateThread) {
+      Thread thread = new Thread(link);
+      thread.setDaemon(true);
+      thread.start();
+    } else {
+      link.run();
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "Pipeline(" + id + ")";
+  }
+
+  /**
+   * 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");
+
+    Pipeline pipeline = new PipelineImpl("main");
+
+    // Create elements
+    pipeline.add(new FakeSource("source") {
+      {
+        this.incommingBufLength = 3;
+        this.numBuffers = 10;
+        this.delay = 100;
+      }
+    });
+    pipeline.add(new BaseElement("tee"));
+    pipeline.add(new FakeSink("sink") {
+      {
+        this.verbose = true;
+      }
+    });
+    pipeline.add(new FakeSink("sink2") {
+      {
+        this.verbose = true;
+      }
+    });
+
+    // Link elements
+    pipeline.link("source", "tee", "sink");
+    pipeline.link("tee >out2", "sink2");
+
+    // Run main loop
+    pipeline.runMainLoop("source", STDOUT, false, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/a98c473d/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java
new file mode 100644
index 0000000..7a17340
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java
@@ -0,0 +1,136 @@
+// 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 streamer;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Message queue for safe transfer of packets between threads.
+ */
+public class Queue extends BaseElement {
+
+  protected LinkedBlockingQueue<ByteBuffer> queue = new LinkedBlockingQueue<ByteBuffer>();
+
+  public Queue(String id) {
+    super(id);
+  }
+
+  @SuppressWarnings("incomplete-switch")
+  @Override
+  public void poll(boolean block) {
+    try {
+      ByteBuffer buf = null;
+      if (block) {
+        buf = queue.take();
+      } else {
+        buf = queue.poll(100, TimeUnit.MILLISECONDS);
+      }
+
+      if (buf != null)
+        pushDataToAllOuts(buf);
+
+    } catch (Exception e) {
+      sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
+      closeQueue();
+    }
+  }
+
+  @Override
+  public void handleData(ByteBuffer buf, Link link) {
+    if (verbose)
+      System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+    // Put incoming data into queue
+    try {
+      queue.put(buf);
+    } catch (Exception e) {
+      sendEventToAllPads(Event.STREAM_CLOSE, Direction.IN);
+      closeQueue();
+    }
+  }
+
+  @Override
+  public void handleEvent(Event event, Direction direction) {
+    switch (event) {
+    case LINK_SWITCH_TO_PULL_MODE:
+      // Do not propagate this event, because this element is boundary between
+      // threads
+      break;
+    default:
+      super.handleEvent(event, direction);
+    }
+  }
+
+  @Override
+  protected void onClose() {
+    super.onClose();
+    closeQueue();
+  }
+
+  private void closeQueue() {
+    queue.clear();
+    queue.add(null);
+    // Drop queue to indicate that upstream is closed.
+    // May produce NPE in poll().
+    queue = null;
+  }
+
+  @Override
+  public String toString() {
+    return "Queue(" + id + ")";
+  }
+
+  /**
+   * Example.
+   */
+  public static void main(String args[]) {
+    // System.setProperty("streamer.Link.debug", "true");
+    System.setProperty("streamer.Element.debug", "true");
+
+    Element source1 = new FakeSource("source1") {
+      {
+        this.delay = 100;
+        this.numBuffers = 10;
+        this.incommingBufLength = 10;
+      }
+    };
+
+    Element source2 = new FakeSource("source2") {
+      {
+        this.delay = 100;
+        this.numBuffers = 10;
+        this.incommingBufLength = 10;
+      }
+    };
+
+    Pipeline pipeline = new PipelineImpl("test");
+    pipeline.add(source1);
+    pipeline.add(source2);
+    pipeline.add(new Queue("queue"));
+    pipeline.add(new FakeSink("sink"));
+
+    // Main flow
+    pipeline.link("source1", "in1< queue");
+    pipeline.link("source2", "in2< queue");
+    pipeline.link("queue", "sink");
+
+    new Thread(pipeline.getLink("source1", STDOUT)).start();
+    new Thread(pipeline.getLink("source2", STDOUT)).start();
+    pipeline.getLink("sink", STDIN).run();
+  }
+}