public class ProtocolVersionDecoder extends ByteToMessageDecoder {

  protected final Charset ASCII = StandardCharsets.US_ASCII;

  private final int length = 12;

  public ProtocolVersionDecoder() {
    setSingleDecode(true);
  }

  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    if (!in.isReadable(length)) {
      return;
    }
    byte[] rfb = new byte[length];
    in.readBytes(rfb);
    String rfbVersion = new String(rfb, ASCII);
    out.add(new ProtocolVersion(rfbVersion));
    ctx.pipeline().remove(this);
  }

} public abstract class RfbClientHandshaker {

  private static Logger logger = LoggerFactory.getLogger(RfbClientHandshaker.class);

  public abstract RfbClientDecoder newRfbClientDecoder();

  public abstract RfbClientEncoder newRfbClientEncoder();

  private AtomicBoolean handshakeComplete = new AtomicBoolean(false);

  private final ProtocolVersion version;

  public RfbClientHandshaker(ProtocolVersion version) {
    this.version = version;
  }

  public boolean isHandshakeComplete() {
    return handshakeComplete.get();
  }

  private void setHandshakeComplete() {
    handshakeComplete.set(true);
  }

  public ChannelFuture handshake(Channel channel) {
    return handshake(channel, channel.newPromise());
  }

  public final ChannelFuture handshake(Channel channel, final ChannelPromise promise) {

    channel.writeAndFlush(Unpooled.wrappedBuffer(version.getBytes())).addListener((ChannelFuture future) -> {
      if (!future.isSuccess()) {
        promise.setFailure(future.cause());
        return;
      }

      ChannelPipeline p = future.channel().pipeline();
      ChannelHandlerContext ctx = p.context(ProtocolHandshakeHandler.class);
      p.addBefore(ctx.name(), "rfb-handshake-decoder", newRfbClientDecoder());
      p.addBefore(ctx.name(), "rfb-handshake-encoder", newRfbClientEncoder());
      promise.setSuccess();

    });
    return promise;
  }

  public final void finishHandshake(Channel channel, ProtocolVersion response) {
    setHandshakeComplete();

    ChannelPipeline p = channel.pipeline();
    p.remove("rfb-handshake-decoder");
    p.remove("rfb-handshake-encoder");

    logger.debug("server {} - client {}", version, response);

  }

} public class RfbClientHandshakerFactory {

  public RfbClientHandshakerFactory() {}

  public RfbClientHandshaker newRfbClientHandshaker(ProtocolVersion version) {

    if (version.equals(ProtocolVersion.RFB_3_8) || version.isGreaterThan(ProtocolVersion.RFB_3_8)) {
      return new RfbClient38Handshaker(version);
    }

    return new RfbClient33Handshaker(version);

  }

} public class SecurityResultEvent implements HandshakeEvent {

  private final boolean passed;
  private Throwable throwable;

  public SecurityResultEvent(boolean passed) {
    this.passed = passed;
  }

  public SecurityResultEvent(boolean passed, Throwable t) {
    this.passed = passed;
    this.setThrowable(t);
  }

  public boolean isPassed() {
    return passed;
  }

  public Throwable getThrowable() {
    return throwable;
  }

  public void setThrowable(Throwable throwable) {
    this.throwable = throwable;
  }

  @Override
  public String toString() {
    return "SecurityResultEvent [passed=" + passed + (throwable != null ? ", throwable=" + throwable.getMessage() : "") + "]";
  }
} public class SecurityTypesEvent implements HandshakeEvent {

  private final boolean response;

  private final SecurityType[] securityTypes;

  public SecurityTypesEvent(boolean response, SecurityType... securityTypes) {
    this.response = response;
    this.securityTypes = securityTypes;
  }

  public SecurityType[] getSecurityTypes() {
    return securityTypes;
  }

  public boolean isResponse() {
    return response;
  }

  @Override
  public String toString() {
    return "SecurityTypesEvent [response=" + response + ", " + (securityTypes != null ? "securityTypes=" + Arrays.toString(securityTypes) : "") + "]";
  }

} public class ServerInitEvent implements HandshakeEvent {

  private int frameBufferWidth;
  private int frameBufferHeight;

  private PixelFormat pixelFormat;

  private String serverName;

  public ServerInitEvent() {}

  public int getFrameBufferWidth() {
    return frameBufferWidth;
  }

  public void setFrameBufferWidth(int frameBufferWidth) {
    this.frameBufferWidth = frameBufferWidth;
  }

  public int getFrameBufferHeight() {
    return frameBufferHeight;
  }

  public void setFrameBufferHeight(int frameBufferHeight) {
    this.frameBufferHeight = frameBufferHeight;
  }

  public PixelFormat getPixelFormat() {
    return pixelFormat;
  }

  public void setPixelFormat(PixelFormat pixelFormat) {
    this.pixelFormat = pixelFormat;
  }

  public String getServerName() {
    return serverName;
  }

  public void setServerName(String serverName) {
    this.serverName = serverName;
  }

  @Override
  public String toString() {
    return "ServerInitEvent [frameBufferWidth=" + frameBufferWidth + ", frameBufferHeight=" + frameBufferHeight + ", pixelFormat=" + pixelFormat
        + ", serverName=" + serverName + "]";
  }

} public class SharedEvent implements HandshakeEvent {

  private final boolean shared;

  public SharedEvent(boolean shared) {
    this.shared = shared;
  }

  public boolean isShared() {
    return shared;
  }

} public abstract class RfbSecurityHandshaker {

  public abstract RfbSecurityDecoder newSecurityDecoder();

  public abstract RfbSecurityEncoder newSecurityEncoder();

  private AtomicBoolean handshakeComplete = new AtomicBoolean(false);

  private final SecurityType securityType;

  public RfbSecurityHandshaker(SecurityType securityType) {
    this.securityType = securityType;
  }

  public boolean isHandshakeComplete() {
    return handshakeComplete.get();
  }

  private void setHandshakeComplete() {
    handshakeComplete.set(true);
  }

  public ChannelFuture handshake(Channel channel, boolean sendResponse) {
    return handshake(channel, sendResponse, channel.newPromise());
  }

  public final ChannelFuture handshake(Channel channel, boolean sendResponse, ChannelPromise promise) {
    ChannelPipeline p = channel.pipeline();
    ChannelHandlerContext ctx = p.context(RfbClientDecoder.class);
    p.addBefore(ctx.name(), "rfb-security-decoder", newSecurityDecoder());

    ChannelHandlerContext ctx2 = p.context(RfbClientEncoder.class);
    p.addBefore(ctx2.name(), "rfb-security-encoder", newSecurityEncoder());
    if (!sendResponse) {
      return promise.setSuccess();
    }
    channel.writeAndFlush(Unpooled.buffer(1).writeByte(securityType.getType()), promise);
    return promise;
  }

  public final void finishHandshake(Channel channel, RfbSecurityMessage message) {
    setHandshakeComplete();

    ChannelPipeline p = channel.pipeline();
    p.remove("rfb-security-decoder");
    p.remove("rfb-security-encoder");

  }

} public class RfbSecurityHandshakerFactory {

  public RfbSecurityHandshakerFactory() {}

  public RfbSecurityHandshaker newRfbSecurityHandshaker(SecurityType securityType) {

    if (securityType == SecurityType.VNC_Auth) {
      return new VncAuthHandshaker(securityType);
    }

    return null;

  }

} public interface RfbSecurityMessage {

  SecurityType getSecurityType();

  void setCredentials(ProtocolConfiguration config);

} public enum SecurityType {

  UNKNOWN(-1), INVALID(0), NONE(1), VNC_Auth(2), RA2(5), RA2ne(6), Tight(16), Ultra(17), TLS(18), VeNCrypt(19), GTK_VNC_SAS(20), MD5(21), Colin_Dean_xvp(22);

  private final int type;

  private SecurityType(int type) {
    this.type = type;
  }

  public static SecurityType valueOf(int type) {
    for (SecurityType e : values()) {
      if (e.type == type) {
        return e;
      }
    }
    return UNKNOWN;
  }

  public int getType() {
    return type;
  }
} public class ProtocolException extends Exception {

  private static final long serialVersionUID = 5616560775184943955L;

  public ProtocolException(String message) {
    super(message);
  }

  public ProtocolException(String message, Throwable throwable) {
    super(message, throwable);
  }
} public interface ProtocolConfiguration {

  final int DEFAULT_PORT = 5900;
  final int DEFAULT_LISTENING_PORT = 5500;

  /**
   * VNC server name or IP address
   * 
   * @return host
   */
  StringProperty hostProperty();

  /**
   * VNC server port (default: 5900)
   * 
   * @return port
   */
  IntegerProperty portProperty();

  /**
   * listening mode to accept incoming connection requests (default: 5500)
   * 
   * @return listening port
   */
  IntegerProperty listeningPortProperty();

  /**
   * VNC authentication password
   * 
   * @return password
   */
  StringProperty passwordProperty();

  /**
   * Enable SSL/TLS transfer
   * 
   * @return SSL/TLS enabled
   */
  BooleanProperty sslProperty();

  /**
   * Security Type {@link SecurityType}
   * 
   * @return current {@link SecurityType}
   * @see org.jfxvnc.net.rfb.codec.security.SecurityType
   */
  ObjectProperty<SecurityType> securityProperty();

  /**
   * VNC connection shared by other clients
   * 
   * @return shared
   */
  BooleanProperty sharedProperty();

  /**
   * Used Protocol Version {@link ProtocolVersion}
   * 
   * @return current {@link ProtocolVersion}
   */
  ObjectProperty<ProtocolVersion> versionProperty();

  /**
   * Used PixelFormat {@link PixelFormat}
   * 
   * @return current {@link PixelFormat}
   * @see org.jfxvnc.net.rfb.codec.PixelFormat
   */
  ObjectProperty<PixelFormat> clientPixelFormatProperty();

  /**
   * Activate RAW encoding
   * 
   * @return raw enabled
   * @see org.jfxvnc.net.rfb.codec.Encoding
   */
  BooleanProperty rawEncProperty();

  /**
   * Activate COPY RECT encoding
   * 
   * @return raw enabled
   * @see org.jfxvnc.net.rfb.codec.Encoding
   */
  BooleanProperty copyRectEncProperty();

  /**
   * Activate Hextile encoding
   * 
   * @return Hextile enabled
   * @see org.jfxvnc.net.rfb.codec.Encoding
   */
  BooleanProperty hextileEncProperty();

  /**
   * Activate Cursor pseudo encoding
   * 
   * @return Cursor enabled
   * @see org.jfxvnc.net.rfb.codec.Encoding
   */
  BooleanProperty clientCursorProperty();

  /**
   * Activate Desktop Resize pseudo encoding
   * 
   * @return Desktop Resize enabled
   * @see org.jfxvnc.net.rfb.codec.Encoding
   */
  BooleanProperty desktopSizeProperty();

  /**
   * Activate Zlib pseudo encoding
   * 
   * @return Zlib enabled
   * @see org.jfxvnc.net.rfb.codec.Encoding
   */
  BooleanProperty zlibEncProperty();

}
import java.util.Arrays;
import org.jfxvnc.net.rfb.codec.decoder.ProtocolVersionDecoder;
import org.jfxvnc.net.rfb.codec.handshaker.RfbClientHandshaker;
import org.jfxvnc.net.rfb.codec.handshaker.RfbClientHandshakerFactory;
import org.jfxvnc.net.rfb.codec.handshaker.event.SecurityResultEvent;
import org.jfxvnc.net.rfb.codec.handshaker.event.SecurityTypesEvent;
import org.jfxvnc.net.rfb.codec.handshaker.event.ServerInitEvent;
import org.jfxvnc.net.rfb.codec.handshaker.event.SharedEvent;
import org.jfxvnc.net.rfb.codec.security.RfbSecurityHandshaker;
import org.jfxvnc.net.rfb.codec.security.RfbSecurityHandshakerFactory;
import org.jfxvnc.net.rfb.codec.security.RfbSecurityMessage;
import org.jfxvnc.net.rfb.codec.security.SecurityType;
import org.jfxvnc.net.rfb.exception.ProtocolException;
import org.jfxvnc.net.rfb.render.ProtocolConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
/*******************************************************************************
 * Copyright (c) 2016 comtel inc.
 *
 * Licensed 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 org.jfxvnc.net.rfb.codec;




public class ProtocolHandshakeHandler extends ChannelInboundHandlerAdapter {

  private static Logger logger = LoggerFactory.getLogger(ProtocolHandshakeHandler.class);

  private RfbClientHandshaker handshaker;

  private RfbSecurityHandshaker secHandshaker;

  private final ProtocolConfiguration config;