public class Common {

	public static boolean underApplet = false;

	public static Rdp5 rdp;

	public static Secure secure;

	public static MCS mcs;

	public static RdesktopFrame frame;

	/**
	 * Quit the application
	 */
	public static void exit() {
		Rdesktop.exit(0, rdp, frame, true);
	}
}
 public class CommunicationMonitor {

	public static Object locker = null;

	/**
	 * Identify whether or not communications are locked
	 * 
	 * @return True if locked
	 */
	public static boolean locked() {
		return locker != null;
	}

	/**
	 * Wait for a lock on communications
	 * 
	 * @param o
	 *            Calling object should supply reference to self
	 */
	public static void lock(Object o) {
		if (locker == null)
			locker = o;
		else {
			while (locker != null) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					System.err.println("InterruptedException: "
							+ e.getMessage());
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * Unlock communications, only permitted if the caller holds the current
	 * lock
	 * 
	 * @param o
	 *            Calling object should supply reference to self
	 * @return
	 */
	public static boolean unlock(Object o) {
		if (locker == o) {
			locker = null;
			return true;
		}
		return false;
	}

}
 public class Constants {

	public static final boolean desktop_save = true;

	// public static final int keylayout = 0x809; // UK... was US, 0x409

	public static final boolean SystemExit = true;

	public static boolean encryption = true;

	public static boolean licence = true;

	public static final int WINDOWS = 1;

	public static final int LINUX = 2;

	public static final int MAC = 3;

	public static int OS = 0;
}
 public abstract class Input {

	protected static Logger logger = Logger.getLogger(Input.class);

	KeyCode_FileBased newKeyMapper = null;

	protected Vector pressedKeys;

	protected static boolean capsLockOn = false;

	protected static boolean numLockOn = false;

	protected static boolean scrollLockOn = false;

	protected static boolean serverAltDown = false;

	protected static boolean altDown = false;

	protected static boolean ctrlDown = false;

	protected static long last_mousemove = 0;

	// Using this flag value (0x0001) seems to do nothing, and after running
	// through other possible values, the RIGHT flag does not appear to be
	// implemented
	protected static final int KBD_FLAG_RIGHT = 0x0001;

	protected static final int KBD_FLAG_EXT = 0x0100;

	// QUIET flag is actually as below (not 0x1000 as in rdesktop)
	protected static final int KBD_FLAG_QUIET = 0x200;

	protected static final int KBD_FLAG_DOWN = 0x4000;

	protected static final int KBD_FLAG_UP = 0x8000;

	protected static final int RDP_KEYPRESS = 0;

	protected static final int RDP_KEYRELEASE = KBD_FLAG_DOWN | KBD_FLAG_UP;

	protected static final int MOUSE_FLAG_MOVE = 0x0800;

	protected static final int MOUSE_FLAG_BUTTON1 = 0x1000;

	protected static final int MOUSE_FLAG_BUTTON2 = 0x2000;

	protected static final int MOUSE_FLAG_BUTTON3 = 0x4000;

	protected static final int MOUSE_FLAG_BUTTON4 = 0x0280; // wheel up -

	// rdesktop 1.2.0
	protected static final int MOUSE_FLAG_BUTTON5 = 0x0380; // wheel down -

	// rdesktop 1.2.0
	protected static final int MOUSE_FLAG_DOWN = 0x8000;

	protected static final int RDP_INPUT_SYNCHRONIZE = 0;

	protected static final int RDP_INPUT_CODEPOINT = 1;

	protected static final int RDP_INPUT_VIRTKEY = 2;

	protected static final int RDP_INPUT_SCANCODE = 4;

	protected static final int RDP_INPUT_MOUSE = 0x8001;

	protected static int time = 0;

	public KeyEvent lastKeyEvent = null;

	public boolean modifiersValid = false;

	public boolean keyDownWindows = false;

	protected RdesktopCanvas canvas = null;

	protected Rdp rdp = null;

	KeyCode keys = null;

	/**
	 * Create a new Input object with a given keymap object
	 * 
	 * @param c
	 *            Canvas on which to listen for input events
	 * @param r
	 *            Rdp layer on which to send input messages
	 * @param k
	 *            Key map to use in handling keyboard events
	 */
	public Input(RdesktopCanvas c, Rdp r, KeyCode_FileBased k) {
		newKeyMapper = k;
		canvas = c;
		rdp = r;
		if (Options.debug_keyboard)
			logger.setLevel(Level.DEBUG);
		addInputListeners();
		pressedKeys = new Vector();
	}

	/**
	 * Create a new Input object, using a keymap generated from a specified file
	 * 
	 * @param c
	 *            Canvas on which to listen for input events
	 * @param r
	 *            Rdp layer on which to send input messages
	 * @param keymapFile
	 *            Path to file containing keymap data
	 */
	public Input(RdesktopCanvas c, Rdp r, String keymapFile) {
		try {
			newKeyMapper = new KeyCode_FileBased_Localised(keymapFile);
		} catch (KeyMapException kmEx) {
			System.err.println(kmEx.getMessage());
			if (!Common.underApplet)
				System.exit(-1);
		}

		canvas = c;
		rdp = r;
		if (Options.debug_keyboard)
			logger.setLevel(Level.DEBUG);
		addInputListeners();
		pressedKeys = new Vector();
	}

	/**
	 * Add all relevant input listeners to the canvas
	 */
	public void addInputListeners() {
		canvas.addMouseListener(new RdesktopMouseAdapter());
		canvas.addMouseMotionListener(new RdesktopMouseMotionAdapter());
		canvas.addKeyListener(new RdesktopKeyAdapter());
	}

	/**
	 * Send a sequence of key actions to the server
	 * 
	 * @param pressSequence
	 *            String representing a sequence of key actions. Actions are
	 *            represented as a pair of consecutive characters, the first
	 *            character's value (cast to integer) being the scancode to
	 *            send, the second (cast to integer) of the pair representing
	 *            the action (0 == UP, 1 == DOWN, 2 == QUIET UP, 3 == QUIET
	 *            DOWN).
	 */
	public void sendKeyPresses(String pressSequence) {
		try {
			String debugString = "Sending keypresses: ";
			for (int i = 0; i < pressSequence.length(); i += 2) {
				int scancode = (int) pressSequence.charAt(i);
				int action = (int) pressSequence.charAt(i + 1);
				int flags = 0;

				if (action == KeyCode_FileBased.UP)
					flags = RDP_KEYRELEASE;
				else if (action == KeyCode_FileBased.DOWN)
					flags = RDP_KEYPRESS;
				else if (action == KeyCode_FileBased.QUIETUP)
					flags = RDP_KEYRELEASE | KBD_FLAG_QUIET;
				else if (action == KeyCode_FileBased.QUIETDOWN)
					flags = RDP_KEYPRESS | KBD_FLAG_QUIET;

				long t = getTime();

				debugString += "(0x"
						+ Integer.toHexString(scancode)
						+ ", "
						+ ((action == KeyCode_FileBased.UP || action == KeyCode_FileBased.QUIETUP) ? "up"
								: "down")
						+ ((flags & KBD_FLAG_QUIET) != 0 ? " quiet" : "")
						+ " at " + t + ")";

				sendScancode(t, flags, scancode);
			}

			if (pressSequence.length() > 0)
				logger.debug(debugString);
		} catch (Exception ex) {
			return;
		}
	}

	/**
	 * Retrieve the next "timestamp", by incrementing previous stamp (up to the
	 * maximum value of an integer, at which the timestamp is reverted to 1)
	 * 
	 * @return New timestamp value
	 */
	public static int getTime() {
		time++;
		if (time == Integer.MAX_VALUE)
			time = 1;
		return time;
	}

	/**
	 * Handle loss of focus to the main canvas. Clears all depressed keys
	 * (sending release messages to the server.
	 */
	public void lostFocus() {
		clearKeys();
		modifiersValid = false;
	}

	/**
	 * Handle the main canvas gaining focus. Check locking key states.
	 */
	public void gainedFocus() {
		doLockKeys(); // ensure lock key states are correct
	}

	/**
	 * Send a keyboard event to the server
	 * 
	 * @param time
	 *            Time stamp to identify this event
	 * @param flags
	 *            Flags defining the nature of the event (eg:
	 *            press/release/quiet/extended)
	 * @param scancode
	 *            Scancode value identifying the key in question
	 */
	public void sendScancode(long time, int flags, int scancode) {

		if (scancode == 0x38) { // be careful with alt
			if ((flags & RDP_KEYRELEASE) != 0) {
				// logger.info("Alt release, serverAltDown = " + serverAltDown);
				serverAltDown = false;
			}
			if ((flags == RDP_KEYPRESS)) {
				// logger.info("Alt press, serverAltDown = " + serverAltDown);
				serverAltDown = true;
			}
		}

		if ((scancode & KeyCode.SCANCODE_EXTENDED) != 0) {
			rdp.sendInput((int) time, RDP_INPUT_SCANCODE, flags | KBD_FLAG_EXT,
					scancode & ~KeyCode.SCANCODE_EXTENDED, 0);
		} else
			rdp.sendInput((int) time, RDP_INPUT_SCANCODE, flags, scancode, 0);
	}

	/**
	 * Release any modifier keys that may be depressed.
	 */
	public void clearKeys() {
		if (!modifiersValid)
			return;

		altDown = false;
		ctrlDown = false;

		if (lastKeyEvent == null)
			return;

		if (lastKeyEvent.isShiftDown())
			sendScancode(getTime(), RDP_KEYRELEASE, 0x2a); // shift
		if (lastKeyEvent.isAltDown() || serverAltDown) {
			sendScancode(getTime(), RDP_KEYRELEASE, 0x38); // ALT
			sendScancode(getTime(), RDP_KEYPRESS | KBD_FLAG_QUIET, 0x38); // ALT
			sendScancode(getTime(), RDP_KEYRELEASE | KBD_FLAG_QUIET, 0x38); // l.alt
		}
		if (lastKeyEvent.isControlDown()) {
			sendScancode(getTime(), RDP_KEYRELEASE, 0x1d); // l.ctrl
			// sendScancode(getTime(), RDP_KEYPRESS | KBD_FLAG_QUIET, 0x1d); //
			// Ctrl
			// sendScancode(getTime(), RDP_KEYRELEASE | KBD_FLAG_QUIET, 0x1d);
			// // ctrl
		}

	}

	/**
	 * Send keypress events for any modifier keys that are currently down
	 */
	public void setKeys() {
		if (!modifiersValid)
			return;

		if (lastKeyEvent == null)
			return;

		if (lastKeyEvent.isShiftDown())
			sendScancode(getTime(), RDP_KEYPRESS, 0x2a); // shift
		if (lastKeyEvent.isAltDown())
			sendScancode(getTime(), RDP_KEYPRESS, 0x38); // l.alt
		if (lastKeyEvent.isControlDown())
			sendScancode(getTime(), RDP_KEYPRESS, 0x1d); // l.ctrl
	}

	class RdesktopKeyAdapter extends KeyAdapter {

		/**
		 * Construct an RdesktopKeyAdapter based on the parent KeyAdapter class
		 */
		public RdesktopKeyAdapter() {
			super();
		}

		/**
		 * Handle a keyPressed event, sending any relevant keypresses to the
		 * server
		 */
		public void keyPressed(KeyEvent e) {
			lastKeyEvent = e;
			modifiersValid = true;
			long time = getTime();

			// Some java versions have keys that don't generate keyPresses -
			// here we add the key so we can later check if it happened
			pressedKeys.addElement(new Integer(e.getKeyCode()));

			logger.debug("PRESSED keychar='" + e.getKeyChar() + "' keycode=0x"
					+ Integer.toHexString(e.getKeyCode()) + " char='"
					+ ((char) e.getKeyCode()) + "'");

			if (rdp != null) {
				if (!handleSpecialKeys(time, e, true)) {
					sendKeyPresses(newKeyMapper.getKeyStrokes(e));
				}
				// sendScancode(time, RDP_KEYPRESS, keys.getScancode(e));
			}
		}

		/**
		 * Handle a keyTyped event, sending any relevant keypresses to the
		 * server
		 */
		public void keyTyped(KeyEvent e) {
			lastKeyEvent = e;
			modifiersValid = true;
			long time = getTime();

			// Some java versions have keys that don't generate keyPresses -
			// here we add the key so we can later check if it happened
			pressedKeys.addElement(new Integer(e.getKeyCode()));

			logger.debug("TYPED keychar='" + e.getKeyChar() + "' keycode=0x"
					+ Integer.toHexString(e.getKeyCode()) + " char='"
					+ ((char) e.getKeyCode()) + "'");

			if (rdp != null) {
				if (!handleSpecialKeys(time, e, true))
					sendKeyPresses(newKeyMapper.getKeyStrokes(e));
				// sendScancode(time, RDP_KEYPRESS, keys.getScancode(e));
			}
		}

		/**
		 * Handle a keyReleased event, sending any relevent key events to the
		 * server
		 */
		public void keyReleased(KeyEvent e) {
			// Some java versions have keys that don't generate keyPresses -
			// we added the key to the vector in keyPressed so here we check for
			// it
			Integer keycode = new Integer(e.getKeyCode());
			if (!pressedKeys.contains(keycode)) {
				this.keyPressed(e);
			}

			pressedKeys.removeElement(keycode);

			lastKeyEvent = e;
			modifiersValid = true;
			long time = getTime();

			logger.debug("RELEASED keychar='" + e.getKeyChar() + "' keycode=0x"
					+ Integer.toHexString(e.getKeyCode()) + " char='"
					+ ((char) e.getKeyCode()) + "'");
			if (rdp != null) {
				if (!handleSpecialKeys(time, e, false))
					sendKeyPresses(newKeyMapper.getKeyStrokes(e));
				// sendScancode(time, RDP_KEYRELEASE, keys.getScancode(e));
			}
		}

	}

	/**
	 * Act on any keyboard shortcuts that a specified KeyEvent may describe
	 * 
	 * @param time
	 *            Time stamp for event to send to server
	 * @param e
	 *            Keyboard event to be checked for shortcut keys
	 * @param pressed
	 *            True if key was pressed, false if released
	 * @return True if a shortcut key combination was detected and acted upon,
	 *         false otherwise
	 */
	public boolean handleShortcutKeys(long time, KeyEvent e, boolean pressed) {
		if (!e.isAltDown())
			return false;

		if (!altDown)
			return false; // all of the below have ALT on

		switch (e.getKeyCode()) {

		/*
		 * case KeyEvent.VK_M: if(pressed) ((RdesktopFrame_Localised)
		 * canvas.getParent()).toggleMenu(); break;
		 */

		case KeyEvent.VK_ENTER:
			sendScancode(time, RDP_KEYRELEASE, 0x38);
			altDown = false;
			((RdesktopFrame_Localised) canvas.getParent()).toggleFullScreen();
			break;

		/*
		 * The below case block handles "real" ALT+TAB events. Once the TAB in
		 * an ALT+TAB combination has been pressed, the TAB is sent to the
		 * server with the quiet flag on, as is the subsequent ALT-up.
		 * 
		 * This ensures that the initial ALT press is "undone" by the server.
		 * 
		 * --- Tom Elliott, 7/04/05
		 */

		case KeyEvent.VK_TAB: // Alt+Tab received, quiet combination

			sendScancode(time, (pressed ? RDP_KEYPRESS : RDP_KEYRELEASE)
					| KBD_FLAG_QUIET, 0x0f);
			if (!pressed) {
				sendScancode(time, RDP_KEYRELEASE | KBD_FLAG_QUIET, 0x38); // Release
				// Alt
			}

			if (pressed)
				logger.debug("Alt + Tab pressed, ignoring, releasing tab");
			break;
		case KeyEvent.VK_PAGE_UP: // Alt + PgUp = Alt-Tab
			sendScancode(time, pressed ? RDP_KEYPRESS : RDP_KEYRELEASE, 0x0f); // TAB
			if (pressed)
				logger.debug("shortcut pressed: sent ALT+TAB");
			break;
		case KeyEvent.VK_PAGE_DOWN: // Alt + PgDown = Alt-Shift-Tab
			if (pressed) {
				sendScancode(time, RDP_KEYPRESS, 0x2a); // Shift
				sendScancode(time, RDP_KEYPRESS, 0x0f); // TAB
				logger.debug("shortcut pressed: sent ALT+SHIFT+TAB");
			} else {
				sendScancode(time, RDP_KEYRELEASE, 0x0f); // TAB
				sendScancode(time, RDP_KEYRELEASE, 0x2a); // Shift
			}

			break;
		case KeyEvent.VK_INSERT: // Alt + Insert = Alt + Esc
			sendScancode(time, pressed ? RDP_KEYPRESS : RDP_KEYRELEASE, 0x01); // ESC
			if (pressed)
				logger.debug("shortcut pressed: sent ALT+ESC");
			break;
		case KeyEvent.VK_HOME: // Alt + Home = Ctrl + Esc (Start)
			if (pressed) {
				sendScancode(time, RDP_KEYRELEASE, 0x38); // ALT
				sendScancode(time, RDP_KEYPRESS, 0x1d); // left Ctrl
				sendScancode(time, RDP_KEYPRESS, 0x01); // Esc
				logger.debug("shortcut pressed: sent CTRL+ESC (Start)");

			} else {
				sendScancode(time, RDP_KEYRELEASE, 0x01); // escape
				sendScancode(time, RDP_KEYRELEASE, 0x1d); // left ctrl
				// sendScancode(time,RDP_KEYPRESS,0x38); // ALT
			}

			break;
		case KeyEvent.VK_END: // Ctrl+Alt+End = Ctrl+Alt+Del
			if (ctrlDown) {
				sendScancode(time, pressed ? RDP_KEYPRESS : RDP_KEYRELEASE,
						0x53 | KeyCode.SCANCODE_EXTENDED); // DEL
				if (pressed)
					logger.debug("shortcut pressed: sent CTRL+ALT+DEL");
			}
			break;
		case KeyEvent.VK_BACK_SPACE: // Ctrl+Alt+BackSpace = Ctrl+Alt+Del
			if (ctrlDown) {
				sendScancode(time, pressed ? RDP_KEYPRESS : RDP_KEYRELEASE,
						0x53 | KeyCode.SCANCODE_EXTENDED); // DEL
				if (pressed)
					logger.debug("shortcut pressed: sent CTRL+ALT+DEL");
			}
			break;
		case KeyEvent.VK_DELETE: // Alt + Delete = Menu
			if (pressed) {
				sendScancode(time, RDP_KEYRELEASE, 0x38); // ALT
				// need to do another press and release to shift focus from
				// to/from menu bar
				sendScancode(time, RDP_KEYPRESS, 0x38); // ALT
				sendScancode(time, RDP_KEYRELEASE, 0x38); // ALT
				sendScancode(time, RDP_KEYPRESS,
						0x5d | KeyCode.SCANCODE_EXTENDED); // Menu
				logger.debug("shortcut pressed: sent MENU");
			} else {
				sendScancode(time, RDP_KEYRELEASE,
						0x5d | KeyCode.SCANCODE_EXTENDED); // Menu
				// sendScancode(time,RDP_KEYPRESS,0x38); // ALT
			}
			break;
		case KeyEvent.VK_SUBTRACT: // Ctrl + Alt + Minus (on NUM KEYPAD) =
			// Alt+PrtSc
			if (ctrlDown) {
				if (pressed) {
					sendScancode(time, RDP_KEYRELEASE, 0x1d); // Ctrl
					sendScancode(time, RDP_KEYPRESS,
							0x37 | KeyCode.SCANCODE_EXTENDED); // PrtSc
					logger.debug("shortcut pressed: sent ALT+PRTSC");
				} else {
					sendScancode(time, RDP_KEYRELEASE,
							0x37 | KeyCode.SCANCODE_EXTENDED); // PrtSc
					sendScancode(time, RDP_KEYPRESS, 0x1d); // Ctrl
				}
			}
			break;
		case KeyEvent.VK_ADD: // Ctrl + ALt + Plus (on NUM KEYPAD) = PrtSc
		case KeyEvent.VK_EQUALS: // for laptops that can't do Ctrl-Alt+Plus
			if (ctrlDown) {
				if (pressed) {
					sendScancode(time, RDP_KEYRELEASE, 0x38); // Alt
					sendScancode(time, RDP_KEYRELEASE, 0x1d); // Ctrl
					sendScancode(time, RDP_KEYPRESS,
							0x37 | KeyCode.SCANCODE_EXTENDED); // PrtSc
					logger.debug("shortcut pressed: sent PRTSC");
				} else {
					sendScancode(time, RDP_KEYRELEASE,
							0x37 | KeyCode.SCANCODE_EXTENDED); // PrtSc
					sendScancode(time, RDP_KEYPRESS, 0x1d); // Ctrl
					sendScancode(time, RDP_KEYPRESS, 0x38); // Alt
				}
			}
			break;
		default:
			return false;
		}
		return true;
	}

	/**
	 * Deal with modifier keys as control, alt or caps lock
	 * 
	 * @param time
	 *            Time stamp for key event
	 * @param e
	 *            Key event to check for special keys
	 * @param pressed
	 *            True if key was pressed, false if released
	 * @return
	 */
	public boolean handleSpecialKeys(long time, KeyEvent e, boolean pressed) {
		if (handleShortcutKeys(time, e, pressed))
			return true;

		switch (e.getKeyCode()) {
		case KeyEvent.VK_CONTROL:
			ctrlDown = pressed;
			return false;
		case KeyEvent.VK_ALT:
			altDown = pressed;
			return false;
		case KeyEvent.VK_CAPS_LOCK:
			if (pressed && Options.caps_sends_up_and_down)
				capsLockOn = !capsLockOn;
			if (!Options.caps_sends_up_and_down) {
				if (pressed)
					capsLockOn = true;
				else
					capsLockOn = false;
			}
			return false;
		case KeyEvent.VK_NUM_LOCK:
			if (pressed)
				numLockOn = !numLockOn;
			return false;
		case KeyEvent.VK_SCROLL_LOCK:
			if (pressed)
				scrollLockOn = !scrollLockOn;
			return false;
		case KeyEvent.VK_PAUSE: // untested
			if (pressed) { // E1 1D 45 E1 9D C5
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYPRESS,
						0xe1, 0);
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYPRESS,
						0x1d, 0);
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYPRESS,
						0x45, 0);
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYPRESS,
						0xe1, 0);
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYPRESS,
						0x9d, 0);
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYPRESS,
						0xc5, 0);
			} else { // release left ctrl
				rdp.sendInput((int) time, RDP_INPUT_SCANCODE, RDP_KEYRELEASE,
						0x1d, 0);
			}
			break;

		// Removed, as java on MacOS send the option key as VK_META
		/*
		 * case KeyEvent.VK_META: // Windows key logger.debug("Windows key
		 * received"); if(pressed){ sendScancode(time, RDP_KEYPRESS, 0x1d); //
		 * left ctrl sendScancode(time, RDP_KEYPRESS, 0x01); // escape } else{
		 * sendScancode(time, RDP_KEYRELEASE, 0x01); // escape
		 * sendScancode(time, RDP_KEYRELEASE, 0x1d); // left ctrl } break;
		 */

		// haven't found a way to detect BREAK key in java - VK_BREAK doesn't
		// exist
		/*
		 * case KeyEvent.VK_BREAK: if(pressed){
		 * sendScancode(time,RDP_KEYPRESS,(KeyCode.SCANCODE_EXTENDED | 0x46));
		 * sendScancode(time,RDP_KEYPRESS,(KeyCode.SCANCODE_EXTENDED | 0xc6)); } //
		 * do nothing on release break;
		 */
		default:
			return false; // not handled - use sendScancode instead
		}
		return true; // handled - no need to use sendScancode
	}

	/**
	 * Turn off any locking key, check states if available
	 */
	public void triggerReadyToSend() {
		capsLockOn = false;
		numLockOn = false;
		scrollLockOn = false;
		doLockKeys(); // ensure lock key states are correct
	}

	protected void doLockKeys() {
	}

	/**
	 * Handle pressing of the middle mouse button, sending relevent event data
	 * to the server
	 * 
	 * @param e
	 *            MouseEvent detailing circumstances under which middle button
	 *            was pressed
	 */
	protected void middleButtonPressed(MouseEvent e) {
		/*
		 * if (Options.paste_hack && ctrlDown){ try{ canvas.setBusyCursor();
		 * }catch (RdesktopException ex){ logger.warn(ex.getMessage()); } if
		 * (capsLockOn){ logger.debug("Turning caps lock off for paste"); //
		 * turn caps lock off sendScancode(getTime(), RDP_KEYPRESS, 0x3a); //
		 * caps lock sendScancode(getTime(), RDP_KEYRELEASE, 0x3a); // caps lock }
		 * paste(); if (capsLockOn){ // turn caps lock back on
		 * logger.debug("Turning caps lock back on after paste");
		 * sendScancode(getTime(), RDP_KEYPRESS, 0x3a); // caps lock
		 * sendScancode(getTime(), RDP_KEYRELEASE, 0x3a); // caps lock }
		 * canvas.unsetBusyCursor(); } else
		 */
		rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_BUTTON3
				| MOUSE_FLAG_DOWN, e.getX(), e.getY());
	}

	/**
	 * Handle release of the middle mouse button, sending relevent event data to
	 * the server
	 * 
	 * @param e
	 *            MouseEvent detailing circumstances under which middle button
	 *            was released
	 */
	protected void middleButtonReleased(MouseEvent e) {
		/* if (!Options.paste_hack || !ctrlDown) */
		rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_BUTTON3, e.getX(), e
				.getY());
	}

	class RdesktopMouseAdapter extends MouseAdapter {

		public RdesktopMouseAdapter() {
			super();
		}

		public void mousePressed(MouseEvent e) {
			if (e.getY() != 0)
				((RdesktopFrame_Localised) canvas.getParent()).hideMenu();

			int time = getTime();
			if (rdp != null) {
				if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
					logger.debug("Mouse Button 1 Pressed.");
					rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_BUTTON1
							| MOUSE_FLAG_DOWN, e.getX(), e.getY());
				} else if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) {
					logger.debug("Mouse Button 3 Pressed.");
					rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_BUTTON2
							| MOUSE_FLAG_DOWN, e.getX(), e.getY());
				} else if ((e.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK) {
					logger.debug("Middle Mouse Button Pressed.");
					middleButtonPressed(e);
				}
			}
		}

		public void mouseReleased(MouseEvent e) {
			int time = getTime();
			if (rdp != null) {
				if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
					rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_BUTTON1, e
							.getX(), e.getY());
				} else if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) {
					rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_BUTTON2, e
							.getX(), e.getY());
				} else if ((e.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK) {
					middleButtonReleased(e);
				}
			}
		}
	}

	class RdesktopMouseMotionAdapter extends MouseMotionAdapter {

		public RdesktopMouseMotionAdapter() {
			super();
		}

		public void mouseMoved(MouseEvent e) {
			int time = getTime();

			// Code to limit mouse events to 4 per second. Doesn't seem to
			// affect performance
			// long mTime = System.currentTimeMillis();
			// if((mTime - Input.last_mousemove) < 250) return;
			// Input.last_mousemove = mTime;

			// if(logger.isInfoEnabled()) logger.info("mouseMoved to
			// "+e.getX()+", "+e.getY()+" at "+time);

			// TODO: complete menu show/hide section
			if (e.getY() == 0)
				((RdesktopFrame_Localised) canvas.getParent()).showMenu();
			// else ((RdesktopFrame_Localised) canvas.getParent()).hideMenu();

			if (rdp != null) {
				rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE, e.getX(),
						e.getY());
			}
		}

		public void mouseDragged(MouseEvent e) {
			int time = getTime();
			// if(logger.isInfoEnabled()) logger.info("mouseMoved to
			// "+e.getX()+", "+e.getY()+" at "+time);
			if (rdp != null) {
				rdp.sendInput(time, RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE, e.getX(),
						e.getY());
			}
		}
	}

}
 public class Options {

	public static final int DIRECT_BITMAP_DECOMPRESSION = 0;

	public static final int BUFFEREDIMAGE_BITMAP_DECOMPRESSION = 1;

	public static final int INTEGER_BITMAP_DECOMPRESSION = 2;

	public static int bitmap_decompression_store = INTEGER_BITMAP_DECOMPRESSION;

	// disables bandwidth saving tcp packets
	public static boolean low_latency = true;

	public static int keylayout = 0x809; // UK by default

	public static String username = "Administrator"; // -u username

	public static String domain = ""; // -d domain

	public static String password = ""; // -p password

	public static String hostname = ""; // -n hostname

	public static String command = ""; // -s command

	public static String directory = ""; // -d directory

	public static String windowTitle = "properJavaRDP"; // -T windowTitle

	public static int width = 800; // -g widthxheight

	public static int height = 600; // -g widthxheight

	public static int port = 3389; // -t port

	public static boolean fullscreen = false;

	public static boolean built_in_licence = false;

	public static boolean load_licence = false;

	public static boolean save_licence = false;

	public static String licence_path = "./";

	public static boolean debug_keyboard = false;

	public static boolean debug_hexdump = false;

	public static boolean enable_menu = false;

	// public static boolean paste_hack = true;

	public static boolean altkey_quiet = false;

	public static boolean caps_sends_up_and_down = true;

	public static boolean remap_hash = true;

	public static boolean useLockingKeyState = true;

	public static boolean use_rdp5 = true;

	public static int server_bpp = 24; // Bits per pixel

	public static int Bpp = (server_bpp + 7) / 8; // Bytes per pixel

	// Correction value to ensure only the relevant number of bytes are used for
	// a pixel
	public static int bpp_mask = 0xFFFFFF >> 8 * (3 - Bpp);

	public static int imgCount = 0;

	public static DirectColorModel colour_model = new DirectColorModel(24,
			0xFF0000, 0x00FF00, 0x0000FF);

	/**
	 * Set a new value for the server's bits per pixel
	 * 
	 * @param server_bpp
	 *            New bpp value
	 */
	public static void set_bpp(int server_bpp) {
		Options.server_bpp = server_bpp;
		Options.Bpp = (server_bpp + 7) / 8;
		if (server_bpp == 8)
			bpp_mask = 0xFF;
		else
			bpp_mask = 0xFFFFFF;

		colour_model = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
	}

	public static int server_rdp_version;

	public static int win_button_size = 0; /* If zero, disable single app mode */

	public static boolean bitmap_compression = true;

	public static boolean persistent_bitmap_caching = false;

	public static boolean bitmap_caching = false;

	public static boolean precache_bitmaps = false;

	public static boolean polygon_ellipse_orders = false;

	public static boolean sendmotion = true;

	public static boolean orders = true;

	public static boolean encryption = true;

	public static boolean packet_encryption = true;

	public static boolean desktop_save = true;

	public static boolean grab_keyboard = true;

	public static boolean hide_decorations = false;

	public static boolean console_session = false;

	public static boolean owncolmap;

	public static boolean use_ssl = false;

	public static boolean map_clipboard = true;

	public static int rdp5_performanceflags = Rdp.RDP5_NO_CURSOR_SHADOW
			| Rdp.RDP5_NO_CURSORSETTINGS | Rdp.RDP5_NO_FULLWINDOWDRAG
			| Rdp.RDP5_NO_MENUANIMATIONS | Rdp.RDP5_NO_THEMING
			| Rdp.RDP5_NO_WALLPAPER;

	public static boolean save_graphics = false;

}
 public class RdesktopException extends Exception {

	private static final long serialVersionUID = 2839110732850220601L;

	public RdesktopException() {
		super();
	}

	public RdesktopException(String s) {
		super(s);
	}
}
 public abstract class RdpPacket {
	static Logger logger = Logger.getLogger(RdpPacket.class);

	/* constants for Packet */
	public static final int MCS_HEADER = 1;

	public static final int SECURE_HEADER = 2;

	public static final int RDP_HEADER = 3;

	public static final int CHANNEL_HEADER = 4;

	protected int mcs = -1;

	protected int secure = -1;

	protected int rdp = -1;

	protected int channel = -1;

	protected int start = -1;

	protected int end = -1;

	/**
	 * Read an 8-bit integer value from the packet (at current read/write
	 * position)
	 * 
	 * @return Value read from packet
	 */
	public abstract int get8();

	/**
	 * Read an 8-bit integer value from a specified offset in the packet
	 * 
	 * @param where
	 *            Offset to read location
	 * @return Value read from packet
	 */
	public abstract int get8(int where);

	/**
	 * Write 8-bit value to packet at current read/write position
	 * 
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void set8(int what);

	/**
	 * Write 8-bit value to packet at specified offset
	 * 
	 * @param where
	 *            Offset in packet to write location
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void set8(int where, int what);

	/**
	 * Read a 2-byte, little-endian integer value from the packet (at current
	 * read/write position)
	 * 
	 * @return Value read from packet
	 */
	public abstract int getLittleEndian16();

	/**
	 * Read a 2-byte, little-endian integer value from a specified offset in the
	 * packet
	 * 
	 * @param where
	 *            Offset to read location
	 * @return Value read from packet
	 */
	public abstract int getLittleEndian16(int where);

	/**
	 * Write a 2-byte, little-endian integer value to packet at current
	 * read/write position
	 * 
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setLittleEndian16(int what);

	/**
	 * Write a 2-byte, little-endian integer value to packet at specified offset
	 * 
	 * @param where
	 *            Offset in packet to write location
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setLittleEndian16(int where, int what);

	/**
	 * Read a 2-byte, big-endian integer value from the packet (at current
	 * read/write position)
	 * 
	 * @return Value read from packet
	 */
	public abstract int getBigEndian16();

	/**
	 * Read a 2-byte, big-endian integer value from a specified offset in the
	 * packet
	 * 
	 * @param where
	 *            Offset to read location
	 * @return Value read from packet
	 */
	public abstract int getBigEndian16(int where);

	/**
	 * Write a 2-byte, big-endian integer value to packet at current read/write
	 * position
	 * 
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setBigEndian16(int what);

	/**
	 * Write a 2-byte, big-endian integer value to packet at specified offset
	 * 
	 * @param where
	 *            Offset in packet to write location
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setBigEndian16(int where, int what);

	/**
	 * Read a 3-byte, little-endian integer value from the packet (at current
	 * read position)
	 * 
	 * @return Value read from packet
	 */
	public abstract int getLittleEndian32();

	/**
	 * Read a 3-byte, little-endian integer value from a specified offset in the
	 * packet
	 * 
	 * @param where
	 *            Offset to read location
	 * @return Value read from packet
	 */
	public abstract int getLittleEndian32(int where);

	/**
	 * Write a 3-byte, little-endian integer value to packet at current
	 * read/write position
	 * 
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setLittleEndian32(int what);

	/**
	 * Write a 3-byte, little-endian integer value to packet at specified offset
	 * 
	 * @param where
	 *            Offset in packet to write location
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setLittleEndian32(int where, int what);

	/**
	 * Read a 3-byte, big-endian integer value from the packet (at current
	 * read/write position)
	 * 
	 * @return Value read from packet
	 */
	public abstract int getBigEndian32();

	/**
	 * Read a 3-byte, big-endian integer value from a specified offset in the
	 * packet
	 * 
	 * @param where
	 *            Offset to read location
	 * @return Value read from packet
	 */
	public abstract int getBigEndian32(int where);

	/**
	 * Write a 3-byte, big-endian integer value to packet at current read/write
	 * position
	 * 
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setBigEndian32(int what);

	/**
	 * Write a 3-byte, big-endian integer value to packet at specified offset
	 * 
	 * @param where
	 *            Offset in packet to write location
	 * @param what
	 *            Value to write to packet
	 */
	public abstract void setBigEndian32(int where, int what);

	/**
	 * Copy data from this packet to an array of bytes
	 * 
	 * @param array
	 *            Array of bytes to which data should be copied
	 * @param array_offset
	 *            Offset into array for start of data
	 * @param mem_offset
	 *            Offset into packet for start of data
	 * @param len
	 *            Length of data to be copied
	 */
	public abstract void copyToByteArray(byte[] array, int array_offset,
			int mem_offset, int len);

	/**
	 * Copy data to this packet from an array of bytes
	 * 
	 * @param array
	 *            Array of bytes containing source data
	 * @param array_offset
	 *            Offset into array for start of data
	 * @param mem_offset
	 *            Offset into packet for start of data
	 * @param len
	 *            Length of data to be copied
	 */
	public abstract void copyFromByteArray(byte[] array, int array_offset,
			int mem_offset, int len);

	/**
	 * Copy data from this packet to another packet
	 * 
	 * @param dst
	 *            Destination packet
	 * @param srcOffset
	 *            Offset into this packet (source) for start of data
	 * @param dstOffset
	 *            Offset into destination packet for start of data
	 * @param len
	 *            Length of data to be copied
	 */
	public abstract void copyToPacket(RdpPacket_Localised dst, int srcOffset,
			int dstOffset, int len);

	/**
	 * Copy data to this packet from another packet
	 * 
	 * @param src
	 *            Source packet
	 * @param srcOffset
	 *            Offset into source packet for start of data
	 * @param dstOffset
	 *            Offset into this packet (destination) for start of data
	 * @param len
	 *            Length of data to be copied
	 */
	public abstract void copyFromPacket(RdpPacket_Localised src, int srcOffset,
			int dstOffset, int len);

	/**
	 * Retrieve size of this packet
	 * 
	 * @return Packet size
	 */
	public abstract int size();

	/**
	 * Retrieve offset to current read/write position
	 * 
	 * @return Current read/write position (as byte offset from start)
	 */
	public abstract int getPosition();

	/**
	 * Set current read/write position
	 * 
	 * @param position
	 *            New read/write position (as byte offset from start)
	 */
	public abstract void setPosition(int position);

	/**
	 * Advance the read/write position
	 * 
	 * @param length
	 *            Number of bytes to advance read position by
	 */
	public abstract void incrementPosition(int length);

	/**
	 * Mark current read/write position as end of packet
	 */
	public void markEnd() {
		this.end = getPosition();
	}

	/**
	 * Retrieve capacity of this packet
	 * 
	 * @return Packet capacity (in bytes)
	 */
	public abstract int capacity();

	/**
	 * Mark specified position as end of packet
	 * 
	 * @param position
	 *            New end position (as byte offset from start)
	 */
	public void markEnd(int position) {
		if (position > capacity()) {
			throw new ArrayIndexOutOfBoundsException("Mark > size!");
		}
		this.end = position;
	}

	/**
	 * Retrieve location of packet end
	 * 
	 * @return Position of packet end (as byte offset from start)
	 */
	public int getEnd() {
		return this.end;
	}

	/**
	 * Reserve space within this packet for writing of headers for a specific
	 * communications layer. Move read/write position ready for adding data for
	 * a higher communications layer.
	 * 
	 * @param header
	 *            ID of header type
	 * @param increment
	 *            Required size to be reserved for header
	 * @throws RdesktopException
	 */
	public void pushLayer(int header, int increment) throws RdesktopException {
		this.setHeader(header);
		this.incrementPosition(increment);
		// this.setStart(this.getPosition());
	}

	/**
	 * Get location of the header for a specific communications layer
	 * 
	 * @param header
	 *            ID of header type
	 * @return Location of header, as byte offset from start of packet
	 * @throws RdesktopException
	 */
	public int getHeader(int header) throws RdesktopException {
		switch (header) {
		case RdpPacket.MCS_HEADER:
			return this.mcs;
		case RdpPacket.SECURE_HEADER:
			return this.secure;
		case RdpPacket.RDP_HEADER:
			return this.rdp;
		case RdpPacket.CHANNEL_HEADER:
			return this.channel;
		default:
			throw new RdesktopException("Wrong Header!");
		}
	}

	/**
	 * Set current read/write position as the start of a layer header
	 * 
	 * @param header
	 *            ID of header type
	 * @throws RdesktopException
	 */
	public void setHeader(int header) throws RdesktopException {
		switch (header) {
		case RdpPacket.MCS_HEADER:
			this.mcs = this.getPosition();
			break;
		case RdpPacket.SECURE_HEADER:
			this.secure = this.getPosition();
			break;
		case RdpPacket.RDP_HEADER:
			this.rdp = this.getPosition();
			break;
		case RdpPacket.CHANNEL_HEADER:
			this.channel = this.getPosition();
			break;
		default:
			throw new RdesktopException("Wrong Header!");
		}
	}

	/**
	 * Retrieve start location of this packet
	 * 
	 * @return Start location of packet (as byte offset from location 0)
	 */
	public int getStart() {
		return this.start;
	}

	/**
	 * Set start position of this packet
	 * 
	 * @param position
	 *            New start position (as byte offset from location 0)
	 */
	public void setStart(int position) {
		this.start = position;
	}

	/**
	 * Add a unicode string to this packet at the current read/write position
	 * 
	 * @param str
	 *            String to write as unicode to packet
	 * @param len
	 *            Desired length of output unicode string
	 */
	public void outUnicodeString(String str, int len) {
		int i = 0, j = 0;

		if (str.length() != 0) {
			char[] name = str.toCharArray();
			while (i < len) {
				this.setLittleEndian16((short) name[j++]);
				i += 2;
			}
			this.setLittleEndian16(0); // Terminating Null Character
		} else {
			this.setLittleEndian16(0);
		}
	}

	/**
	 * Write an ASCII string to this packet at current read/write position
	 * 
	 * @param str
	 *            String to be written
	 * @param length
	 *            Length in bytes to be occupied by string (may be longer than
	 *            string itself)
	 */
	public void out_uint8p(String str, int length) {
		byte[] bStr = str.getBytes();
		this.copyFromByteArray(bStr, 0, this.getPosition(), bStr.length);
		this.incrementPosition(length);
	}
}
 public class RdpPacket_Localised extends RdpPacket {

	private ByteBuffer bb = null;

	private int size = 0;

	public RdpPacket_Localised(int capacity) {
		bb = ByteBuffer.allocateDirect(capacity);
		size = capacity;
	}

	public void reset(int length) {
		// logger.info("RdpPacket_Localised.reset(" + length + "), capacity = "
		// + bb.capacity());
		this.end = 0;
		this.start = 0;
		if (bb.capacity() < length)
			bb = ByteBuffer.allocateDirect(length);
		size = length;
		bb.clear();
	}

	public void set8(int where, int what) {
		if (where < 0 || where >= bb.capacity()) {
			throw new ArrayIndexOutOfBoundsException(
					"memory accessed out of Range!");
		}
		bb.put(where, (byte) what);
	}

	public void set8(int what) {
		if (bb.position() >= bb.capacity()) {
			throw new ArrayIndexOutOfBoundsException(
					"memory accessed out of Range!");
		}
		bb.put((byte) what);
	}

	// where is a 8-bit offset
	public int get8(int where) {
		if (where < 0 || where >= bb.capacity()) {
			throw new ArrayIndexOutOfBoundsException(
					"memory accessed out of Range!");
		}
		return bb.get(where) & 0xff; // treat as unsigned byte
	}

	// where is a 8-bit offset
	public int get8() {
		if (bb.position() >= bb.capacity()) {
			throw new ArrayIndexOutOfBoundsException(
					"memory accessed out of Range!");
		}
		return bb.get() & 0xff; // treat as unsigned byte
	}

	public void copyFromByteArray(byte[] array, int array_offset,
			int mem_offset, int len) {
		if ((array_offset >= array.length)
				|| (array_offset + len > array.length)
				|| (mem_offset + len > bb.capacity())) {
			throw new ArrayIndexOutOfBoundsException(
					"memory accessed out of Range!");
		}
		// store position
		int oldpos = getPosition();

		setPosition(mem_offset);
		bb.put(array, array_offset, len);

		// restore position
		setPosition(oldpos);
	}

	public void copyToByteArray(byte[] array, int array_offset, int mem_offset,
			int len) {
		if ((array_offset >= array.length))
			throw new ArrayIndexOutOfBoundsException(
					"Array offset beyond end of array!");
		if (array_offset + len > array.length)
			throw new ArrayIndexOutOfBoundsException(
					"Not enough bytes in array to copy!");
		if (mem_offset + len > bb.capacity())
			throw new ArrayIndexOutOfBoundsException(
					"Memory accessed out of Range!");

		int oldpos = getPosition();
		setPosition(mem_offset);
		bb.get(array, array_offset, len);
		setPosition(oldpos);
	}

	public void copyToPacket(RdpPacket_Localised dst, int srcOffset,
			int dstOffset, int len) {
		int olddstpos = dst.getPosition();
		int oldpos = getPosition();
		dst.setPosition(dstOffset);
		setPosition(srcOffset);
		for (int i = 0; i < len; i++)
			dst.set8(bb.get());
		dst.setPosition(olddstpos);
		setPosition(oldpos);
	}

	public void copyFromPacket(RdpPacket_Localised src, int srcOffset,
			int dstOffset, int len) {
		int oldsrcpos = src.getPosition();
		int oldpos = getPosition();
		src.setPosition(srcOffset);
		setPosition(dstOffset);
		for (int i = 0; i < len; i++)
			bb.put((byte) src.get8());
		src.setPosition(oldsrcpos);
		setPosition(oldpos);
	}

	public int capacity() {
		return bb.capacity();
	}

	// return size in bytes
	public int size() {
		return size;
		// return bb.capacity(); //this.end - this.start;
	}

	public int getPosition() {
		return bb.position();
	}

	public int getLittleEndian16(int where) {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		return bb.getShort(where);
	}

	public int getLittleEndian16() {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		return bb.getShort();
	}

	public int getBigEndian16(int where) {
		bb.order(ByteOrder.BIG_ENDIAN);
		return bb.getShort(where);
	}

	public int getBigEndian16() {
		bb.order(ByteOrder.BIG_ENDIAN);
		return bb.getShort();
	}

	public void setLittleEndian16(int where, int what) {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.putShort(where, (short) what);
	}

	public void setLittleEndian16(int what) {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.putShort((short) what);
	}

	public void setBigEndian16(int where, int what) {
		bb.order(ByteOrder.BIG_ENDIAN);
		bb.putShort(where, (short) what);
	}

	public void setBigEndian16(int what) {
		bb.order(ByteOrder.BIG_ENDIAN);
		bb.putShort((short) what);
	}

	public int getLittleEndian32(int where) {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		return bb.getInt(where);
	}

	public int getLittleEndian32() {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		return bb.getInt();
	}

	public int getBigEndian32(int where) {
		bb.order(ByteOrder.BIG_ENDIAN);
		return bb.getInt(where);
	}

	public int getBigEndian32() {
		bb.order(ByteOrder.BIG_ENDIAN);
		return bb.getInt();
	}

	public void setLittleEndian32(int where, int what) {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.putInt(where, what);
	}

	public void setLittleEndian32(int what) {
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.putInt(what);
	}

	public void setBigEndian32(int where, int what) {
		bb.order(ByteOrder.BIG_ENDIAN);
		bb.putInt(where, what);
	}

	public void setBigEndian32(int what) {
		bb.order(ByteOrder.BIG_ENDIAN);
		bb.putInt(what);
	}

	public void incrementPosition(int length) {

		if (length > bb.capacity() || length + bb.position() > bb.capacity()
				|| length < 0) {
			throw new ArrayIndexOutOfBoundsException();
		}
		bb.position(bb.position() + length);
	}

	public void setPosition(int position) {
		if (position > bb.capacity() || position < 0) {
			logger.warn("stream position =" + getPosition() + " end ="
					+ getEnd() + " capacity =" + capacity());
			logger.warn("setPosition(" + position + ") failed");
			throw new ArrayIndexOutOfBoundsException();
		}
		bb.position(position);
	}

}
 public class Secure {
	boolean readCert = false;

	static Logger logger = Logger.getLogger(Secure.class);

	private Licence licence = new Licence(this);

	/* constants for the secure layer */
	public static final int SEC_ENCRYPT = 0x0008;

	public static final int SEC_LOGON_INFO = 0x0040;

	static final int SEC_RANDOM_SIZE = 32;

	static final int SEC_MODULUS_SIZE = 64;

	static final int SEC_MAX_MODULUS_SIZE = 256;

	static final int SEC_PADDING_SIZE = 8;

	private static final int SEC_EXPONENT_SIZE = 4;

	private static final int SEC_CLIENT_RANDOM = 0x0001;

	static final int SEC_LICENCE_NEG = 0x0080;

	private static final int SEC_TAG_SRV_INFO = 0x0c01;

	private static final int SEC_TAG_SRV_CRYPT = 0x0c02;

	private static final int SEC_TAG_SRV_3 = 0x0c03;

	private static final int SEC_TAG_SRV_CHANNELS = 0x0c03;

	private static final int SEC_TAG_CLI_INFO = 0xc001;

	private static final int SEC_TAG_CLI_CRYPT = 0xc002;

	private static final int SEC_TAG_CLI_CHANNELS = 0xc003;

	private static final int SEC_TAG_CLI_4 = 0xc004;

	private static final int SEC_TAG_PUBKEY = 0x0006;

	private static final int SEC_TAG_KEYSIG = 0x0008;

	private static final int SEC_RSA_MAGIC = 0x31415352; /* RSA1 */

	private MCS McsLayer = null;

	// private String hostname=null;
	// private String username=null;
	boolean licenceIssued = false;

	private RC4 rc4_enc = null;

	private RC4 rc4_dec = null;

	private RC4 rc4_update = null;

	private BlockMessageDigest sha1 = null;

	private BlockMessageDigest md5 = null;

	private int keylength = 0;

	private int enc_count = 0;

	private int dec_count = 0;

	private int server_public_key_len = 0;

	private byte[] sec_sign_key = null;

	private byte[] sec_decrypt_key = null;

	private byte[] sec_encrypt_key = null;

	private byte[] sec_decrypt_update_key = null;

	private byte[] sec_encrypt_update_key = null;

	private byte[] sec_crypted_random = null;

	private byte[] exponent = null;

	private byte[] modulus = null;

	private byte[] server_random = null;

	private byte[] client_random = new byte[SEC_RANDOM_SIZE];

	private static final byte[] pad_54 = { 54, 54, 54, 54, 54, 54, 54, 54, 54,
			54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
			54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 };

	private static final byte[] pad_92 = { 92, 92, 92, 92, 92, 92, 92, 92, 92,
			92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
			92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
			92, 92, 92, 92, 92 };

	private VChannels channels;

	/**
	 * Initialise Secure layer of communications
	 * 
	 * @param channels
	 *            Virtual channels for this connection
	 */
	public Secure(VChannels channels) {
		this.channels = channels;
		McsLayer = new MCS(channels);
		Common.mcs = McsLayer;
		rc4_dec = new RC4();
		rc4_enc = new RC4();
		rc4_update = new RC4();
		sha1 = new SHA1();
		md5 = new MD5();
		sec_sign_key = new byte[16]; // changed from 8 - rdesktop 1.2.0
		sec_decrypt_key = new byte[16];
		sec_encrypt_key = new byte[16];
		sec_decrypt_update_key = new byte[16]; // changed from 8 - rdesktop
		// 1.2.0
		sec_encrypt_update_key = new byte[16]; // changed from 8 - rdesktop
		// 1.2.0
		sec_crypted_random = new byte[64];

	}

	/**
	 * Connect to server
	 * 
	 * @param host
	 *            Address of server to connect to
	 * @param port
	 *            Port to connect to
	 * @throws UnknownHostException
	 * @throws IOException
	 * @throws RdesktopException
	 * @throws SocketException
	 * @throws CryptoException
	 * @throws OrderException
	 */
	public void connect(InetAddress host, int port)
			throws UnknownHostException, IOException, RdesktopException,
			SocketException, CryptoException, OrderException {
		if (Options.hostname == "") {
			InetAddress localhost = InetAddress.getLocalHost();
			String name = localhost.getHostName();
			StringTokenizer tok = new StringTokenizer(name, ".");
			Options.hostname = tok.nextToken();
			Options.hostname.trim();
		}

		RdpPacket_Localised mcs_data = this.sendMcsData();
		McsLayer.connect(host, port, mcs_data);

		this.processMcsData(mcs_data);

		if (Constants.encryption) {
			this.establishKey();
		}
	}

	/**
	 * Connect to server on default port
	 * 
	 * @param host
	 *            Server to connect to
	 * @throws IOException
	 * @throws RdesktopException
	 * @throws OrderException
	 * @throws CryptoException
	 */
	public void connect(InetAddress host) throws IOException,
			RdesktopException, OrderException, CryptoException {
		this.connect(host, Options.port);
	}

	/**
	 * Close connection
	 */
	public void disconnect() {
		McsLayer.disconnect();
	}

	/**
	 * Construct MCS data, including channel, encryption and display options
	 * 
	 * @return Packet populated with MCS data
	 */
	public RdpPacket_Localised sendMcsData() {
		logger.debug("Secure.sendMcsData");

		RdpPacket_Localised buffer = new RdpPacket_Localised(512);

		int hostlen = 2 * (Options.hostname == null ? 0 : Options.hostname
				.length());

		if (hostlen > 30) {
			hostlen = 30;
		}

		int length = 158;
		if (Options.use_rdp5)
			length += 76 + 12 + 4;

		if (Options.use_rdp5 && (channels.num_channels() > 0))
			length += channels.num_channels() * 12 + 8;

		buffer.setBigEndian16(5); /* unknown */
		buffer.setBigEndian16(0x14);
		buffer.set8(0x7c);
		buffer.setBigEndian16(1);

		buffer.setBigEndian16(length | 0x8000); // remaining length

		buffer.setBigEndian16(8); // length?
		buffer.setBigEndian16(16);
		buffer.set8(0);
		buffer.setLittleEndian16(0xc001);
		buffer.set8(0);

		buffer.setLittleEndian32(0x61637544); // "Duca" ?!
		buffer.setBigEndian16(length - 14 | 0x8000); // remaining length

		// Client information
		buffer.setLittleEndian16(SEC_TAG_CLI_INFO);
		buffer.setLittleEndian16(Options.use_rdp5 ? 212 : 136); // length
		buffer.setLittleEndian16(Options.use_rdp5 ? 4 : 1);
		buffer.setLittleEndian16(8);
		buffer.setLittleEndian16(Options.width);
		buffer.setLittleEndian16(Options.height);
		buffer.setLittleEndian16(0xca01);
		buffer.setLittleEndian16(0xaa03);
		buffer.setLittleEndian32(Options.keylayout);
		buffer.setLittleEndian32(Options.use_rdp5 ? 2600 : 419); // or 0ece
		// // client
		// build? we
		// are 2600
		// compatible
		// :-)

		/* Unicode name of client, padded to 32 bytes */
		buffer.outUnicodeString(Options.hostname.toUpperCase(), hostlen);
		buffer.incrementPosition(30 - hostlen);

		buffer.setLittleEndian32(4);
		buffer.setLittleEndian32(0);
		buffer.setLittleEndian32(12);
		buffer.incrementPosition(64); /* reserved? 4 + 12 doublewords */

		buffer.setLittleEndian16(0xca01); // out_uint16_le(s, 0xca01);
		buffer.setLittleEndian16(Options.use_rdp5 ? 1 : 0);

		if (Options.use_rdp5) {
			buffer.setLittleEndian32(0); // out_uint32(s, 0);
			buffer.set8(Options.server_bpp); // out_uint8(s, g_server_bpp);
			buffer.setLittleEndian16(0x0700); // out_uint16_le(s, 0x0700);
			buffer.set8(0); // out_uint8(s, 0);
			buffer.setLittleEndian32(1); // out_uint32_le(s, 1);

			buffer.incrementPosition(64);

			buffer.setLittleEndian16(SEC_TAG_CLI_4); // out_uint16_le(s,
			// SEC_TAG_CLI_4);
			buffer.setLittleEndian16(12); // out_uint16_le(s, 12);
			buffer.setLittleEndian32(Options.console_session ? 0xb : 0xd); // out_uint32_le(s,
			// g_console_session
			// ?
			// 0xb
			// :
			// 9);
			buffer.setLittleEndian32(0); // out_uint32(s, 0);
		}

		// Client encryption settings //
		buffer.setLittleEndian16(SEC_TAG_CLI_CRYPT);
		buffer.setLittleEndian16(Options.use_rdp5 ? 12 : 8); // length

		// if(Options.use_rdp5) buffer.setLittleEndian32(Options.encryption ?
		// 0x1b : 0); // 128-bit encryption supported
		// else
		buffer
				.setLittleEndian32(Options.encryption ? (Options.console_session ? 0xb
						: 0x3)
						: 0);

		if (Options.use_rdp5)
			buffer.setLittleEndian32(0); // unknown

		if (Options.use_rdp5 && (channels.num_channels() > 0)) {
			logger.debug(("num_channels is " + channels.num_channels()));
			buffer.setLittleEndian16(SEC_TAG_CLI_CHANNELS); // out_uint16_le(s,
			// SEC_TAG_CLI_CHANNELS);
			buffer.setLittleEndian16(channels.num_channels() * 12 + 8); // out_uint16_le(s,
			// g_num_channels
			// * 12
			// + 8);
			// //
			// length
			buffer.setLittleEndian32(channels.num_channels()); // out_uint32_le(s,
			// g_num_channels);
			// // number of
			// virtual
			// channels
			for (int i = 0; i < channels.num_channels(); i++) {
				logger.debug(("Requesting channel " + channels.channel(i)
						.name()));
				buffer.out_uint8p(channels.channel(i).name(), 8); // out_uint8a(s,
				// g_channels[i].name,
				// 8);
				buffer.setBigEndian32(channels.channel(i).flags()); // out_uint32_be(s,
				// g_channels[i].flags);
			}
		}

		buffer.markEnd();
		return buffer;
	}

	/**
	 * Handle MCS info from server (server info, encryption info and channel
	 * information)
	 * 
	 * @param mcs_data
	 *            Data received from server
	 */
	public void processMcsData(RdpPacket_Localised mcs_data)
			throws RdesktopException, CryptoException {
		logger.debug("Secure.processMcsData");
		int tag = 0, len = 0, length = 0, nexttag = 0;

		mcs_data.incrementPosition(21); // header (T.124 stuff, probably)
		len = mcs_data.get8();

		if ((len & 0x00000080) != 0) {
			len = mcs_data.get8();
		}

		while (mcs_data.getPosition() < mcs_data.getEnd()) {
			tag = mcs_data.getLittleEndian16();
			length = mcs_data.getLittleEndian16();

			if (length <= 4)
				return;

			nexttag = mcs_data.getPosition() + length - 4;

			switch (tag) {
			case (Secure.SEC_TAG_SRV_INFO):
				processSrvInfo(mcs_data);
				break;
			case (Secure.SEC_TAG_SRV_CRYPT):
				this.processCryptInfo(mcs_data);
				break;
			case (Secure.SEC_TAG_SRV_CHANNELS):
				/*
				 * FIXME: We should parse this information and use it to map
				 * RDP5 channels to MCS channels
				 */
				break;

			default:
				throw new RdesktopException("Not implemented! Tag:" + tag
						+ "not recognized!");
			}

			mcs_data.setPosition(nexttag);
		}
	}

	/**
	 * Read server info from packet, specifically the RDP version of the server
	 * 
	 * @param mcs_data
	 *            Packet to read
	 */
	private void processSrvInfo(RdpPacket_Localised mcs_data) {
		Options.server_rdp_version = mcs_data.getLittleEndian16(); // in_uint16_le(s,
		// g_server_rdp_version);
		logger.debug(("Server RDP version is " + Options.server_rdp_version));
		if (1 == Options.server_rdp_version)
			Options.use_rdp5 = false;
	}

	public void establishKey() throws RdesktopException, IOException,
			CryptoException {
		int length = server_public_key_len + SEC_PADDING_SIZE;
		int flags = SEC_CLIENT_RANDOM;
		RdpPacket_Localised buffer = this.init(flags, length + 4);

		buffer.setLittleEndian32(length);

		buffer.copyFromByteArray(this.sec_crypted_random, 0, buffer
				.getPosition(), server_public_key_len);
		buffer.incrementPosition(server_public_key_len);
		buffer.incrementPosition(SEC_PADDING_SIZE);
		buffer.markEnd();
		this.send(buffer, flags);

	}

	public void processCryptInfo(RdpPacket_Localised data)
			throws RdesktopException, CryptoException {
		int rc4_key_size = 0;

		rc4_key_size = this.parseCryptInfo(data);
		if (rc4_key_size == 0) {
			return;
		}

		// this.client_random = this.generateRandom(SEC_RANDOM_SIZE);
		logger.debug("readCert = " + readCert);
		if (readCert) { /*
						 * Which means we should use RDP5-style encryption
						 */

			// *** reverse the client random
			// this.reverse(this.client_random);

			// *** load the server public key into the stored data for
			// encryption
			/*
			 * this.exponent =
			 * this.server_public_key.getPublicExponent().toByteArray();
			 * this.modulus = this.server_public_key.getModulus().toByteArray();
			 * 
			 * System.out.println("Exponent: " +
			 * server_public_key.getPublicExponent());
			 * System.out.println("Modulus: " + server_public_key.getModulus());
			 */

			// *** perform encryption
			// this.sec_crypted_random = RSA_public_encrypt(this.client_random,
			// this.server_public_key);
			// this.RSAEncrypt(SEC_RANDOM_SIZE);
			// this.RSAEncrypt(SEC_RANDOM_SIZE);
			// *** reverse the random data back
			// this.reverse(this.sec_crypted_random);
		} else {
			this.generateRandom();
			this.RSAEncrypt(SEC_RANDOM_SIZE, server_public_key_len);
		}
		this.generate_keys(rc4_key_size);
	}

	/**
	 * Intialise a packet at the Secure layer
	 * 
	 * @param flags
	 *            Encryption flags
	 * @param length
	 *            Length of packet
	 * @return Intialised packet
	 * @throws RdesktopException
	 */
	public RdpPacket_Localised init(int flags, int length)
			throws RdesktopException {
		int headerlength = 0;
		RdpPacket_Localised buffer;

		if (!this.licenceIssued)
			headerlength = ((flags & SEC_ENCRYPT) != 0) ? 12 : 4;
		else
			headerlength = ((flags & SEC_ENCRYPT) != 0) ? 12 : 0;

		buffer = McsLayer.init(length + headerlength);
		buffer.pushLayer(RdpPacket.SECURE_HEADER, headerlength);
		// buffer.setHeader(RdpPacket_Localised.SECURE_HEADER);
		// buffer.incrementPosition(headerlength);
		// buffer.setStart(buffer.getPosition());
		return buffer;
	}

	/**
	 * Send secure data on the global channel
	 * 
	 * @param sec_data
	 *            Data to send
	 * @param flags
	 *            Encryption flags
	 * @throws RdesktopException
	 * @throws IOException
	 * @throws CryptoException
	 */
	public void send(RdpPacket_Localised sec_data, int flags)
			throws RdesktopException, IOException, CryptoException {
		send_to_channel(sec_data, flags, MCS.MCS_GLOBAL_CHANNEL);
	}

	/**
	 * Prepare data as a Secure PDU and pass down to the MCS layer
	 * 
	 * @param sec_data
	 *            Data to send
	 * @param flags
	 *            Encryption flags
	 * @param channel
	 *            Channel over which to send data
	 * @throws RdesktopException
	 * @throws IOException
	 * @throws CryptoException
	 */
	public void send_to_channel(RdpPacket_Localised sec_data, int flags,
			int channel) throws RdesktopException, IOException, CryptoException {
		int datalength = 0;
		byte[] signature = null;
		byte[] data;
		byte[] buffer;

		sec_data.setPosition(sec_data.getHeader(RdpPacket.SECURE_HEADER));

		if (this.licenceIssued == false || (flags & SEC_ENCRYPT) != 0) {
			sec_data.setLittleEndian32(flags);
		}
		if ((flags & SEC_ENCRYPT) != 0) {
			flags &= ~SEC_ENCRYPT;
			datalength = sec_data.getEnd() - sec_data.getPosition() - 8;
			data = new byte[datalength];
			buffer = null;
			sec_data.copyToByteArray(data, 0, sec_data.getPosition() + 8,
					datalength);
			signature = this.sign(this.sec_sign_key, 8, this.keylength, data,
					datalength);

			buffer = this.encrypt(data, datalength);

			sec_data.copyFromByteArray(signature, 0, sec_data.getPosition(), 8);
			sec_data.copyFromByteArray(buffer, 0, sec_data.getPosition() + 8,
					datalength);

		}
		// McsLayer.send(sec_data);
		McsLayer.send_to_channel(sec_data, channel);
	}

	/**
	 * Generate MD5 signature
	 * 
	 * @param session_key
	 *            Key with which to sign data
	 * @param length
	 *            Length of signature
	 * @param keylen
	 *            Length of key
	 * @param data
	 *            Data to sign
	 * @param datalength
	 *            Length of data to sign
	 * @return Signature for data
	 * @throws CryptoException
	 */
	public byte[] sign(byte[] session_key, int length, int keylen, byte[] data,
			int datalength) throws CryptoException {
		byte[] shasig = new byte[20];
		byte[] md5sig = new byte[16];
		byte[] lenhdr = new byte[4];
		byte[] signature = new byte[length];

		this.setLittleEndian32(lenhdr, datalength);

		sha1.engineReset();
		sha1.engineUpdate(session_key, 0, keylen/* length */);
		sha1.engineUpdate(pad_54, 0, 40);
		sha1.engineUpdate(lenhdr, 0, 4);
		sha1.engineUpdate(data, 0, datalength);
		shasig = sha1.engineDigest();
		sha1.engineReset();

		md5.engineReset();
		md5.engineUpdate(session_key, 0, keylen/* length */);
		md5.engineUpdate(pad_92, 0, 48);
		md5.engineUpdate(shasig, 0, 20);
		md5sig = md5.engineDigest();
		md5.engineReset();

		System.arraycopy(md5sig, 0, signature, 0, length);
		return signature;
	}

	/**
	 * Encrypt specified number of bytes from provided data using RC4 algorithm
	 * 
	 * @param data
	 *            Data to encrypt
	 * @param length
	 *            Number of bytes to encrypt (from start of array)
	 * @return Encrypted data
	 * @throws CryptoException
	 */
	public byte[] encrypt(byte[] data, int length) throws CryptoException {
		byte[] buffer = null;
		if (this.enc_count == 4096) {
			sec_encrypt_key = this.update(this.sec_encrypt_key,
					this.sec_encrypt_update_key);
			byte[] key = new byte[this.keylength];
			System.arraycopy(this.sec_encrypt_key, 0, key, 0, this.keylength);
			this.rc4_enc.engineInitEncrypt(key);
			// logger.debug("Packet enc_count="+enc_count);
			this.enc_count = 0;
		}
		// this.rc4.engineInitEncrypt(this.rc4_encrypt_key);
		buffer = this.rc4_enc.crypt(data, 0, length);
		this.enc_count++;
		return buffer;
	}

	/**
	 * Encrypt provided data using the RC4 algorithm
	 * 
	 * @param data
	 *            Data to encrypt
	 * @return Encrypted data
	 * @throws CryptoException
	 */
	public byte[] encrypt(byte[] data) throws CryptoException {
		byte[] buffer = null;
		if (this.enc_count == 4096) {
			sec_encrypt_key = this.update(this.sec_encrypt_key,
					this.sec_encrypt_update_key);
			byte[] key = new byte[this.keylength];
			System.arraycopy(this.sec_encrypt_key, 0, key, 0, this.keylength);
			this.rc4_enc.engineInitEncrypt(key);
			// logger.debug("Packet enc_count="+enc_count);
			this.enc_count = 0;
		}
		// this.rc4.engineInitEncrypt(this.rc4_encrypt_key);

		buffer = this.rc4_enc.crypt(data);
		this.enc_count++;
		return buffer;
	}

	/**
	 * Decrypt specified number of bytes from provided data using RC4 algorithm
	 * 
	 * @param data
	 *            Data to decrypt
	 * @param length
	 *            Number of bytes to decrypt (from start of array)
	 * @return Decrypted data
	 * @throws CryptoException
	 */
	public byte[] decrypt(byte[] data, int length) throws CryptoException {
		byte[] buffer = null;
		if (this.dec_count == 4096) {
			sec_decrypt_key = this.update(this.sec_decrypt_key,
					this.sec_decrypt_update_key);
			byte[] key = new byte[this.keylength];
			System.arraycopy(this.sec_decrypt_key, 0, key, 0, this.keylength);
			this.rc4_dec.engineInitDecrypt(key);
			// logger.debug("Packet dec_count="+dec_count);
			this.dec_count = 0;
		}
		// this.rc4.engineInitDecrypt(this.rc4_decrypt_key);
		buffer = this.rc4_dec.crypt(data, 0, length);
		this.dec_count++;
		return buffer;
	}

	/**
	 * Decrypt provided data using RC4 algorithm
	 * 
	 * @param data
	 *            Data to decrypt
	 * @return Decrypted data
	 * @throws CryptoException
	 */
	public byte[] decrypt(byte[] data) throws CryptoException {
		byte[] buffer = null;
		if (this.dec_count == 4096) {
			sec_decrypt_key = this.update(this.sec_decrypt_key,
					this.sec_decrypt_update_key);
			byte[] key = new byte[this.keylength];
			System.arraycopy(this.sec_decrypt_key, 0, key, 0, this.keylength);
			this.rc4_dec.engineInitDecrypt(key);
			// logger.debug("Packet dec_count="+dec_count);
			this.dec_count = 0;
		}
		// this.rc4.engineInitDecrypt(this.rc4_decrypt_key);

		buffer = this.rc4_dec.crypt(data);
		this.dec_count++;
		return buffer;
	}

	/**
	 * Read encryption information from a Secure layer PDU, obtaining and
	 * storing level of encryption and any keys received
	 * 
	 * @param data
	 *            Packet to read encryption information from
	 * @return Size of RC4 key
	 * @throws RdesktopException
	 */
	public int parseCryptInfo(RdpPacket_Localised data)
			throws RdesktopException {
		logger.debug("Secure.parseCryptInfo");
		int encryption_level = 0, random_length = 0, RSA_info_length = 0;
		int tag = 0, length = 0;
		int next_tag = 0, end = 0;
		int rc4_key_size = 0;

		rc4_key_size = data.getLittleEndian32(); // 1 = 40-Bit 2 = 128 Bit
		encryption_level = data.getLittleEndian32(); // 1 = low, 2 = medium,
		// 3 = high
		if (encryption_level == 0) { // no encryption
			return 0;
		}
		random_length = data.getLittleEndian32();
		RSA_info_length = data.getLittleEndian32();

		if (random_length != SEC_RANDOM_SIZE) {
			throw new RdesktopException("Wrong Size of Random! Got"
					+ random_length + "expected" + SEC_RANDOM_SIZE);
		}
		this.server_random = new byte[random_length];
		data.copyToByteArray(this.server_random, 0, data.getPosition(),
				random_length);
		data.incrementPosition(random_length);

		end = data.getPosition() + RSA_info_length;

		if (end > data.getEnd()) {
			logger.debug("Reached end of crypt info prematurely ");
			return 0;
		}

		// data.incrementPosition(12); // unknown bytes
		int flags = data.getLittleEndian32(); // in_uint32_le(s, flags); // 1
		// = RDP4-style, 0x80000002 =
		// X.509
		logger.debug("Flags = 0x" + Integer.toHexString(flags));
		if ((flags & 1) != 0) {
			logger.debug(("We're going for the RDP4-style encryption"));
			data.incrementPosition(8); // in_uint8s(s, 8); // unknown

			while (data.getPosition() < data.getEnd()) {
				tag = data.getLittleEndian16();
				length = data.getLittleEndian16();

				next_tag = data.getPosition() + length;

				switch (tag) {

				case (Secure.SEC_TAG_PUBKEY):

					if (!parsePublicKey(data)) {
						return 0;
					}

					break;
				case (Secure.SEC_TAG_KEYSIG):
					// Microsoft issued a key but we don't care
					break;

				default:
					throw new RdesktopException("Unimplemented decrypt tag "
							+ tag);
				}
				data.setPosition(next_tag);
			}

			if (data.getPosition() == data.getEnd()) {
				return rc4_key_size;
			} else {
				logger.warn("End not reached!");
				return 0;
			}

		} else {
			// data.incrementPosition(4); // number of certificates
			int num_certs = data.getLittleEndian32();

			int cacert_len = data.getLittleEndian32();
			data.incrementPosition(cacert_len);
			int cert_len = data.getLittleEndian32();
			data.incrementPosition(cert_len);

			readCert = true;

			return rc4_key_size;
		}

	}

	/*
	 * public X509Certificate readCert(int length, RdpPacket_Localised data){
	 * byte[] buf = new byte[length];
	 * 
	 * data.copyToByteArray(buf,0,data.getPosition(),buf.length);
	 * data.incrementPosition(length);
	 * 
	 * for(int i = 0; i < buf.length; i++){ buf[i] = (byte) (buf[i] & 0xFF); }
	 * 
	 * ByteArrayInputStream bIn = new ByteArrayInputStream(buf); X509Certificate
	 * cert = null; CertificateFactory cf = null; try { cf =
	 * CertificateFactory.getInstance("X.509"); cert =
	 * (X509Certificate)cf.generateCertificate(bIn); } catch
	 * (CertificateException e) { // TODO Auto-generated catch block
	 * e.printStackTrace(); }
	 * 
	 * bIn.reset(); return cert; }
	 */
	public void generateRandom() {
		/*
		 * try{ SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
		 * random.nextBytes(this.client_random); }
		 * catch(NoSuchAlgorithmException e){logger.warn("No Such Random
		 * Algorithm");}
		 */
	}

	public void RSAEncrypt(int length, int modulus_size)
			throws RdesktopException {
		byte[] inr = new byte[length];
		// int outlength = 0;
		BigInteger mod = null;
		BigInteger exp = null;
		BigInteger x = null;

		this.reverse(this.exponent);
		this.reverse(this.modulus);
		System.arraycopy(this.client_random, 0, inr, 0, length);
		this.reverse(inr);

		if ((this.modulus[0] & 0x80) != 0) {
			byte[] temp = new byte[this.modulus.length + 1];
			System.arraycopy(this.modulus, 0, temp, 1, this.modulus.length);
			temp[0] = 0;
			mod = new BigInteger(temp);
		} else {
			mod = new BigInteger(this.modulus);
		}
		if ((this.exponent[0] & 0x80) != 0) {
			byte[] temp = new byte[this.exponent.length + 1];
			System.arraycopy(this.exponent, 0, temp, 1, this.exponent.length);
			temp[0] = 0;
			exp = new BigInteger(temp);
		} else {
			exp = new BigInteger(this.exponent);
		}
		if ((inr[0] & 0x80) != 0) {
			byte[] temp = new byte[inr.length + 1];
			System.arraycopy(inr, 0, temp, 1, inr.length);
			temp[0] = 0;
			x = new BigInteger(temp);
		} else {
			x = new BigInteger(inr);
		}

		BigInteger y = x.modPow(exp, mod);
		this.sec_crypted_random = y.toByteArray();

		if ((this.sec_crypted_random[0] & 0x80) != 0) {
			throw new RdesktopException(
					"Wrong Sign! Expected positive Integer!");
		}

		if (this.sec_crypted_random.length > SEC_MAX_MODULUS_SIZE) {
			logger.warn("sec_crypted_random too big!"); /* FIXME */
		}
		this.reverse(this.sec_crypted_random);

		byte[] temp = new byte[SEC_MAX_MODULUS_SIZE];

		if (this.sec_crypted_random.length < modulus_size) {
			System.arraycopy(this.sec_crypted_random, 0, temp, 0,
					this.sec_crypted_random.length);
			for (int i = this.sec_crypted_random.length; i < temp.length; i++) {
				temp[i] = 0;
			}
			this.sec_crypted_random = temp;

		}

	}

	/**
	 * Read in a public key from a provided Secure layer PDU, and store in
	 * this.exponent and this.modulus
	 * 
	 * @param data
	 *            Secure layer PDU containing key data
	 * @return True if key successfully read
	 * @throws RdesktopException
	 */
	public boolean parsePublicKey(RdpPacket_Localised data)
			throws RdesktopException {
		int magic = 0, modulus_length = 0;

		magic = data.getLittleEndian32();

		if (magic != SEC_RSA_MAGIC) {
			throw new RdesktopException("Wrong magic! Expected" + SEC_RSA_MAGIC
					+ "got:" + magic);
		}

		modulus_length = data.getLittleEndian32() - SEC_PADDING_SIZE;

		if (modulus_length < 64 || modulus_length > SEC_MAX_MODULUS_SIZE) {
			throw new RdesktopException("Bad server public key size ("
					+ (modulus_length * 8) + " bites)");
		}

		data.incrementPosition(8); // unknown modulus bits
		this.exponent = new byte[SEC_EXPONENT_SIZE];
		data.copyToByteArray(this.exponent, 0, data.getPosition(),
				SEC_EXPONENT_SIZE);
		data.incrementPosition(SEC_EXPONENT_SIZE);
		this.modulus = new byte[modulus_length];
		data.copyToByteArray(this.modulus, 0, data.getPosition(),
				modulus_length);
		data.incrementPosition(modulus_length);
		data.incrementPosition(SEC_PADDING_SIZE);
		this.server_public_key_len = modulus_length;

		if (data.getPosition() <= data.getEnd()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Reverse the values in the provided array
	 * 
	 * @param data
	 *            Array as passed reversed on return
	 */
	public void reverse(byte[] data) {
		int i = 0, j = 0;
		byte temp = 0;

		for (i = 0, j = data.length - 1; i < j; i++, j--) {
			temp = data[i];
			data[i] = data[j];
			data[j] = temp;
		}
	}

	public void reverse(byte[] data, int length) {
		int i = 0, j = 0;
		byte temp = 0;

		for (i = 0, j = length - 1; i < j; i++, j--) {
			temp = data[i];
			data[i] = data[j];
			data[j] = temp;
		}
	}

	public byte[] hash48(byte[] in, byte[] salt1, byte[] salt2, int salt)
			throws CryptoException {
		byte[] shasig = new byte[20];
		byte[] pad = new byte[4];
		byte[] out = new byte[48];
		int i = 0;

		for (i = 0; i < 3; i++) {
			for (int j = 0; j <= i; j++) {
				pad[j] = (byte) (salt + i);
			}
			sha1.engineUpdate(pad, 0, i + 1);
			sha1.engineUpdate(in, 0, 48);
			sha1.engineUpdate(salt1, 0, 32);
			sha1.engineUpdate(salt2, 0, 32);
			shasig = sha1.engineDigest();
			sha1.engineReset();

			md5.engineUpdate(in, 0, 48);
			md5.engineUpdate(shasig, 0, 20);
			System.arraycopy(md5.engineDigest(), 0, out, i * 16, 16);
		}

		return out;
	}

	public byte[] hash16(byte[] in, byte[] salt1, byte[] salt2, int in_position)
			throws CryptoException {

		md5.engineUpdate(in, in_position, 16);
		md5.engineUpdate(salt1, 0, 32);
		md5.engineUpdate(salt2, 0, 32);
		return md5.engineDigest();
	}

	/**
	 * Generate a 40-bit key and store in the parameter key.
	 * 
	 * @param key
	 */
	public void make40bit(byte[] key) {
		key[0] = (byte) 0xd1;
		key[1] = (byte) 0x26;
		key[2] = (byte) 0x9e;
	}

	/**
	 * 
	 * @param key
	 * @param update_key
	 * @return
	 * @throws CryptoException
	 */
	public byte[] update(byte[] key, byte[] update_key) throws CryptoException {
		byte[] shasig = new byte[20];
		byte[] update = new byte[this.keylength]; // changed from 8 - rdesktop
		// 1.2.0
		byte[] thekey = new byte[key.length];

		sha1.engineReset();
		sha1.engineUpdate(update_key, 0, keylength);
		sha1.engineUpdate(pad_54, 0, 40);
		sha1.engineUpdate(key, 0, keylength); // changed from 8 - rdesktop
		// 1.2.0
		shasig = sha1.engineDigest();
		sha1.engineReset();

		md5.engineReset();
		md5.engineUpdate(update_key, 0, keylength); // changed from 8 - rdesktop
		// 1.2.0
		md5.engineUpdate(pad_92, 0, 48);
		md5.engineUpdate(shasig, 0, 20);
		thekey = md5.engineDigest();
		md5.engineReset();

		System.arraycopy(thekey, 0, update, 0, this.keylength);
		rc4_update.engineInitDecrypt(update);
		// added
		thekey = rc4_update.crypt(thekey, 0, this.keylength);

		if (this.keylength == 8) {
			this.make40bit(thekey);
		}

		return thekey;
	}

	/**
	 * Write a 32-bit integer value to an array of bytes, length 4
	 * 
	 * @param data
	 *            Modified by method to be a 4-byte array representing the
	 *            parameter value
	 * @param value
	 *            Integer value to return as a little-endian 32-bit value
	 */
	public void setLittleEndian32(byte[] data, int value) {

		data[3] = (byte) ((value >>> 24) & 0xff);
		data[2] = (byte) ((value >>> 16) & 0xff);
		data[1] = (byte) ((value >>> 8) & 0xff);
		data[0] = (byte) (value & 0xff);
	}

	/**
	 * Receive a Secure layer PDU from the MCS layer
	 * 
	 * @return Packet representing received Secure PDU
	 * @throws RdesktopException
	 * @throws IOException
	 * @throws CryptoException
	 * @throws OrderException
	 */
	public RdpPacket_Localised receive() throws RdesktopException, IOException,
			CryptoException, OrderException {
		int sec_flags = 0;
		RdpPacket_Localised buffer = null;
		while (true) {
			int[] channel = new int[1];
			buffer = McsLayer.receive(channel);
			if (buffer == null)
				return null;
			buffer.setHeader(RdpPacket.SECURE_HEADER);
			if (Constants.encryption || (!this.licenceIssued)) {

				sec_flags = buffer.getLittleEndian32();

				if ((sec_flags & SEC_LICENCE_NEG) != 0) {
					licence.process(buffer);
					continue;
				}
				if ((sec_flags & SEC_ENCRYPT) != 0) {
					buffer.incrementPosition(8); // signature
					byte[] data = new byte[buffer.size() - buffer.getPosition()];
					buffer.copyToByteArray(data, 0, buffer.getPosition(),
							data.length);
					byte[] packet = this.decrypt(data);

					buffer.copyFromByteArray(packet, 0, buffer.getPosition(),
							packet.length);

					// buffer.setStart(buffer.getPosition());
					// return buffer;
				}
			}

			if (channel[0] != MCS.MCS_GLOBAL_CHANNEL) {
				channels.channel_process(buffer, channel[0]);
				continue;
			}

			buffer.setStart(buffer.getPosition());
			return buffer;
		}
	}

	/**
	 * Generate encryption keys of applicable size for connection
	 * 
	 * @param rc4_key_size
	 *            Size of keys to generate (1 if 40-bit encryption, otherwise
	 *            128-bit)
	 * @throws CryptoException
	 */
	public void generate_keys(int rc4_key_size) throws CryptoException {
		byte[] session_key = new byte[48];
		byte[] temp_hash = new byte[48];
		byte[] input = new byte[48];

		System.arraycopy(this.client_random, 0, input, 0, 24);
		System.arraycopy(this.server_random, 0, input, 24, 24);

		temp_hash = this.hash48(input, this.client_random, this.server_random,
				65);
		session_key = this.hash48(temp_hash, this.client_random,
				this.server_random, 88);

		System.arraycopy(session_key, 0, this.sec_sign_key, 0, 16);
		// changed from 8 - rdesktop 1.2.0

		this.sec_decrypt_key = this.hash16(session_key, this.client_random,
				this.server_random, 16);
		this.sec_encrypt_key = this.hash16(session_key, this.client_random,
				this.server_random, 32);

		if (rc4_key_size == 1) {
			logger.info("40 Bit Encryption enabled");
			this.make40bit(this.sec_sign_key);
			this.make40bit(this.sec_decrypt_key);
			this.make40bit(this.sec_encrypt_key);
			this.keylength = 8;
		} else {
			logger.info("128 Bit Encryption enabled");
			this.keylength = 16;
		}

		System.arraycopy(this.sec_decrypt_key, 0, this.sec_decrypt_update_key,
				0, 16); // changed from 8 - rdesktop 1.2.0
		System.arraycopy(this.sec_encrypt_key, 0, this.sec_encrypt_update_key,
				0, 16); // changed from 8 - rdesktop 1.2.0

		byte[] key = new byte[this.keylength];
		System.arraycopy(this.sec_encrypt_key, 0, key, 0, this.keylength);
		rc4_enc.engineInitEncrypt(key);
		System.arraycopy(this.sec_decrypt_key, 0, key, 0, this.keylength);
		rc4_dec.engineInitDecrypt(key);
	}

	/**
	 * @return MCS user ID
	 */
	public int getUserID() {
		return McsLayer.getUserID();
	}
} public class CryptoException extends Exception {
	private static final long serialVersionUID = -5302220269148556116L;

	public CryptoException() {
		super();
	}

	/**
	 * @param reason
	 *            the reason why the exception was thrown.
	 */
	public CryptoException(String reason) {
		super(reason);
	}
}
 public abstract class VChannel {

	protected static Logger logger = Logger.getLogger(Input.class);

	private int mcs_id = 0;

	/**
	 * Provide the name of this channel
	 * 
	 * @return Channel name as string
	 */
	public abstract String name();

	/**
	 * Provide the set of flags specifying working options for this channel
	 * 
	 * @return Option flags
	 */
	public abstract int flags();

	/**
	 * Process a packet sent on this channel
	 * 
	 * @param data
	 *            Packet sent to this channel
	 * @throws RdesktopException
	 * @throws IOException
	 * @throws CryptoException
	 */
	public abstract void process(RdpPacket data) throws RdesktopException,
			IOException, CryptoException;

	public int mcs_id() {
		return mcs_id;
	}

	/**
	 * Set the MCS ID for this channel
	 * 
	 * @param mcs_id
	 *            New MCS ID
	 */
	public void set_mcs_id(int mcs_id) {
		this.mcs_id = mcs_id;
	}

	/**
	 * Initialise a packet for transmission over this virtual channel
	 * 
	 * @param length
	 *            Desired length of packet
	 * @return Packet prepared for this channel
	 * @throws RdesktopException
	 */
	public RdpPacket_Localised init(int length) throws RdesktopException {
		RdpPacket_Localised s;

		s = Common.secure.init(Options.encryption ? Secure.SEC_ENCRYPT : 0,
				length + 8);
		s.setHeader(RdpPacket.CHANNEL_HEADER);
		s.incrementPosition(8);

		return s;
	}

	/**
	 * Send a packet over this virtual channel
	 * 
	 * @param data
	 *            Packet to be sent
	 * @throws RdesktopException
	 * @throws IOException
	 * @throws CryptoException
	 */
	public void send_packet(RdpPacket_Localised data) throws RdesktopException,
			IOException, CryptoException {
		if (Common.secure == null)
			return;
		int length = data.size();

		int data_offset = 0;
		int packets_sent = 0;
		int num_packets = (length / VChannels.CHANNEL_CHUNK_LENGTH);
		num_packets += length - (VChannels.CHANNEL_CHUNK_LENGTH) * num_packets;

		while (data_offset < length) {

			int thisLength = Math.min(VChannels.CHANNEL_CHUNK_LENGTH, length
					- data_offset);

			RdpPacket_Localised s = Common.secure.init(
					Constants.encryption ? Secure.SEC_ENCRYPT : 0,
					8 + thisLength);
			s.setLittleEndian32(length);

			int flags = ((data_offset == 0) ? VChannels.CHANNEL_FLAG_FIRST : 0);
			if (data_offset + thisLength >= length)
				flags |= VChannels.CHANNEL_FLAG_LAST;

			if ((this.flags() & VChannels.CHANNEL_OPTION_SHOW_PROTOCOL) != 0)
				flags |= VChannels.CHANNEL_FLAG_SHOW_PROTOCOL;

			s.setLittleEndian32(flags);
			s.copyFromPacket(data, data_offset, s.getPosition(), thisLength);
			s.incrementPosition(thisLength);
			s.markEnd();

			data_offset += thisLength;

			if (Common.secure != null)
				Common.secure.send_to_channel(s,
						Constants.encryption ? Secure.SEC_ENCRYPT : 0, this
								.mcs_id());
			packets_sent++;
		}
	}

}
 public class VChannels {

	protected static Logger logger = Logger.getLogger(Input.class);

	/* Sound format constants */
	public static final int WAVE_FORMAT_PCM = 1;

	public static final int WAVE_FORMAT_ADPCM = 2;

	public static final int WAVE_FORMAT_ALAW = 6;

	public static final int WAVE_FORMAT_MULAW = 7;

	/* Virtual channel options */
	public static final int CHANNEL_OPTION_INITIALIZED = 0x80000000;

	public static final int CHANNEL_OPTION_ENCRYPT_RDP = 0x40000000;

	public static final int CHANNEL_OPTION_COMPRESS_RDP = 0x00800000;

	public static final int CHANNEL_OPTION_SHOW_PROTOCOL = 0x00200000;

	/* NT status codes for RDPDR */
	public static final int STATUS_SUCCESS = 0x00000000;

	public static final int STATUS_INVALID_PARAMETER = 0xc000000d;

	public static final int STATUS_INVALID_DEVICE_REQUEST = 0xc0000010;

	public static final int STATUS_ACCESS_DENIED = 0xc0000022;

	public static final int MAX_CHANNELS = 4;

	public static final int CHANNEL_CHUNK_LENGTH = 1600;

	public static final int CHANNEL_FLAG_FIRST = 0x01;

	public static final int CHANNEL_FLAG_LAST = 0x02;

	public static final int CHANNEL_FLAG_SHOW_PROTOCOL = 0x10;

	private VChannel channels[] = new VChannel[MAX_CHANNELS];

	private int num_channels;

	public int num_channels() {
		return num_channels;
	}

	private byte[] fragment_buffer = null;

	/**
	 * Obtain the MCS ID for a specific numbered channel
	 * 
	 * @param c
	 *            Channel number for which to obtain MCS ID
	 * @return MCS ID associated with the supplied channel number
	 */
	public int mcs_id(int c) {
		return MCS.MCS_GLOBAL_CHANNEL + 1 + c;
	}

	/**
	 * Initialise the maximum number of Virtual Channels
	 */
	public VChannels() {
		channels = new VChannel[MAX_CHANNELS];
	}

	/**
	 * Retrieve the VChannel object for the numbered channel
	 * 
	 * @param c
	 *            Channel number
	 * @return The requested Virtual Channel
	 */
	public VChannel channel(int c) {
		if (c < num_channels)
			return channels[c];
		else
			return null;
	}

	/**
	 * Retrieve the VChannel object for the specified MCS channel ID
	 * 
	 * @param channelno
	 *            MCS ID for the required channel
	 * @return Virtual Channel associated with the supplied MCS ID
	 */
	public VChannel find_channel_by_channelno(int channelno) {
		if (channelno > MCS.MCS_GLOBAL_CHANNEL + num_channels) {
			logger.warn("Channel " + channelno
					+ " not defined. Highest channel defined is "
					+ MCS.MCS_GLOBAL_CHANNEL + num_channels);
			return null;
		} else
			return channels[channelno - MCS.MCS_GLOBAL_CHANNEL - 1];
	}

	/**
	 * Remove all registered virtual channels
	 */
	public void clear() {
		channels = new VChannel[MAX_CHANNELS];
		num_channels = 0;
	}

	/**
	 * Register a new virtual channel
	 * 
	 * @param v
	 *            Virtual channel to be registered
	 * @return True if successful
	 * @throws RdesktopException
	 */
	public boolean register(VChannel v) throws RdesktopException {
		if (!Options.use_rdp5) {
			return false;
		}

		if (num_channels >= MAX_CHANNELS)
			throw new RdesktopException(
					"Channel table full. Could not register channel.");

		channels[num_channels] = v;
		v.set_mcs_id(MCS.MCS_GLOBAL_CHANNEL + 1 + num_channels);
		num_channels++;

		return true;
	}

	/**
	 * Process a packet sent on a numbered channel
	 * 
	 * @param data
	 *            Packet sent to channel
	 * @param mcsChannel
	 *            Number specified for channel
	 * @throws RdesktopException
	 * @throws IOException
	 * @throws CryptoException
	 */
	public void channel_process(RdpPacket_Localised data, int mcsChannel)
			throws RdesktopException, IOException, CryptoException {

		int length, flags;
		VChannel channel = null;

		int i;

		for (i = 0; i < num_channels; i++) {
			if (mcs_id(i) == mcsChannel) {
				channel = channels[i];
				break;
			}
		}

		if (i >= num_channels)
			return;

		length = data.getLittleEndian32();
		flags = data.getLittleEndian32();

		if (((flags & CHANNEL_FLAG_FIRST) != 0)
				&& ((flags & CHANNEL_FLAG_LAST) != 0)) {
			// single fragment - pass straight up
			channel.process(data);
		} else {
			// append to the defragmentation buffer
			byte[] content = new byte[data.getEnd() - data.getPosition()];
			data
					.copyToByteArray(content, 0, data.getPosition(),
							content.length);
			fragment_buffer = append(fragment_buffer, content);

			if ((flags & CHANNEL_FLAG_LAST) != 0) {
				RdpPacket_Localised fullpacket = new RdpPacket_Localised(
						fragment_buffer.length);
				fullpacket.copyFromByteArray(fragment_buffer, 0, 0,
						fragment_buffer.length);
				// process the entire reconstructed packet
				channel.process(fullpacket);
				fragment_buffer = null;
			}

		}
	}

	/**
	 * Increase the size of an array
	 * 
	 * @param a
	 *            Array to expand
	 * @param amount
	 *            Number of elements to add to the array
	 * @return Expanded array
	 */
	static Object arrayExpand(Object a, int amount) {
		Class cl = a.getClass();
		if (!cl.isArray())
			return null;
		int length = Array.getLength(a);
		int newLength = length + amount; // 50% more

		Class componentType = a.getClass().getComponentType();
		Object newArray = Array.newInstance(componentType, newLength);
		System.arraycopy(a, 0, newArray, 0, length);
		return newArray;
	}

	/**
	 * Concatenate two byte arrays
	 * 
	 * @param target
	 *            Contains initial bytes in output
	 * @param source
	 *            Appended to target array
	 * @return Concatenation of arrays, target+source
	 */
	static byte[] append(byte[] target, byte[] source) {
		if (target == null || target.length <= 0)
			return source;
		else if (source == null || source.length <= 0)
			return target;
		else {
			byte[] out = (byte[]) arrayExpand(target, source.length);
			System.arraycopy(source, 0, out, target.length, source.length);
			return out;
		}
	}
}

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.IOException;
import java.util.Iterator;
import net.propero.rdp.Common;
import net.propero.rdp.CommunicationMonitor;
import net.propero.rdp.Constants;
import net.propero.rdp.Input;
import net.propero.rdp.Options;
import net.propero.rdp.RdesktopException;
import net.propero.rdp.RdpPacket;
import net.propero.rdp.RdpPacket_Localised;
import net.propero.rdp.Secure;
import net.propero.rdp.crypto.CryptoException;
import net.propero.rdp.rdp5.VChannel;
import net.propero.rdp.rdp5.VChannels;
import org.apache.log4j.Logger;

/* ClipChannel.java
 * Component: ProperJavaRDP
 * 
 * Revision: $Revision: 13 $
 * Author: $Author: miha_vitorovic $
 * Date: $Date: 2007-05-11 14:14:45 +0200 (Fri, 11 May 2007) $
 *
 * Copyright (c) 2005 Propero Limited
 *
 * Purpose: 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 * 
 * (See gpl.txt for details of the GNU General Public License.)
 * 
 */
package net.propero.rdp.rdp5.cliprdr;




public class ClipChannel extends VChannel implements ClipInterface,
		ClipboardOwner, FocusListener {

	String[] types = { "unused", "CF_TEXT", "CF_BITMAP", "CF_METAFILEPICT",
			"CF_SYLK", "CF_DIF", "CF_TIFF", "CF_OEMTEXT", "CF_DIB",
			"CF_PALETTE", "CF_PENDATA", "CF_RIFF", "CF_WAVE", "CF_UNICODETEXT",
			"CF_ENHMETAFILE", "CF_HDROP", "CF_LOCALE", "CF_MAX" };

	protected static Logger logger = Logger.getLogger(Input.class);

	// Message types
	public static final int CLIPRDR_CONNECT = 1;

	public static final int CLIPRDR_FORMAT_ANNOUNCE = 2;

	public static final int CLIPRDR_FORMAT_ACK = 3;

	public static final int CLIPRDR_DATA_REQUEST = 4;

	public static final int CLIPRDR_DATA_RESPONSE = 5;

	// Message status codes
	public static final int CLIPRDR_REQUEST = 0;

	public static final int CLIPRDR_RESPONSE = 1;

	public static final int CLIPRDR_ERROR = 2;

	Clipboard clipboard;

	// TypeHandler for data currently being awaited
	TypeHandler currentHandler = null;

	// All type handlers available
	TypeHandlerList allHandlers = null;

	byte[] localClipData = null;

	public ClipChannel() {
		this.clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

		// initialise all clipboard format handlers
		allHandlers = new TypeHandlerList();
		allHandlers.add(new UnicodeHandler());
		allHandlers.add(new TextHandler());
		allHandlers.add(new DIBHandler());
		// allHandlers.add(new MetafilepictHandler());
	}

	/*
	 * VChannel inherited abstract methods
	 */
	public String name() {
		return "cliprdr";
	}

	public int flags() {
		return VChannels.CHANNEL_OPTION_INITIALIZED
				| VChannels.CHANNEL_OPTION_ENCRYPT_RDP
				| VChannels.CHANNEL_OPTION_COMPRESS_RDP
				| VChannels.CHANNEL_OPTION_SHOW_PROTOCOL;
	}

	/*
	 * Data processing methods
	 */
	public void process(RdpPacket data) throws RdesktopException, IOException,
			CryptoException {

		int type, status;
		int length;

		type = data.getLittleEndian16();
		status = data.getLittleEndian16();
		length = data.getLittleEndian32();

		if (status == CLIPRDR_ERROR) {
			if (type == CLIPRDR_FORMAT_ACK) {
				send_format_announce();
				return;
			}

			return;
		}

		switch (type) {
		case CLIPRDR_CONNECT:
			send_format_announce();
			break;
		case CLIPRDR_FORMAT_ANNOUNCE:
			handle_clip_format_announce(data, length);
			return;
		case CLIPRDR_FORMAT_ACK:
			break;
		case CLIPRDR_DATA_REQUEST:
			handle_data_request(data);
			break;
		case CLIPRDR_DATA_RESPONSE:
			handle_data_response(data, length);
			break;
		case 7:
			break;
		default:
			// System.out.println("Unimplemented packet type! " + type);
		}

	}

	public void send_null(int type, int status) {

		RdpPacket_Localised s;
