/* ClipChannel.java * Component: ProperJavaRDP * * Revision: $Revision: 1.4 $ * Author: $Author: telliott $ * Date: $Date: 2005/09/27 14:15:40 $ * * 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 org.jopenray.rdp.rdp5.cliprdr; 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 org.apache.log4j.Logger; import org.jopenray.rdp.Common; import org.jopenray.rdp.CommunicationMonitor; import org.jopenray.rdp.Constants; import org.jopenray.rdp.Input; import org.jopenray.rdp.Options; import org.jopenray.rdp.RdesktopException; import org.jopenray.rdp.RdpPacket; import org.jopenray.rdp.RdpPacket_Localised; import org.jopenray.rdp.Secure; import org.jopenray.rdp.crypto.CryptoException; import org.jopenray.rdp.rdp5.VChannel; import org.jopenray.rdp.rdp5.VChannels; 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, format; 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; s = new RdpPacket_Localised(12); s.setLittleEndian16(type); s.setLittleEndian16(status); s.setLittleEndian32(0); s.setLittleEndian32(0); // pad s.markEnd(); try { this.send_packet(s); } catch (RdesktopException e) { System.err.println(e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.err.println(e.getMessage()); e.printStackTrace(); } catch (CryptoException e) { System.err.println(e.getMessage()); e.printStackTrace(); } } void send_format_announce() throws RdesktopException, IOException, CryptoException { Transferable clipData = clipboard.getContents(clipboard); DataFlavor[] dataTypes = clipData.getTransferDataFlavors(); TypeHandlerList availableFormats = allHandlers .getHandlersForClipboard(dataTypes); RdpPacket_Localised s; int number_of_formats = availableFormats.count(); s = new RdpPacket_Localised(number_of_formats * 36 + 12); s.setLittleEndian16(CLIPRDR_FORMAT_ANNOUNCE); s.setLittleEndian16(CLIPRDR_REQUEST); s.setLittleEndian32(number_of_formats * 36); TypeHandler handler = null; for (Iterator i = availableFormats.iterator(); i.hasNext();) { handler = (TypeHandler) i.next(); s.setLittleEndian32(handler.preferredFormat()); s.incrementPosition(32); } s.setLittleEndian32(0); // pad s.markEnd(); send_packet(s); } private void handle_clip_format_announce(RdpPacket data, int length) throws RdesktopException, IOException, CryptoException { TypeHandlerList serverTypeList = new TypeHandlerList(); // System.out.print("Available types: "); for (int c = length; c >= 36; c -= 36) { int typeCode = data.getLittleEndian32(); // if(typeCode < types.length) System.out.print(types[typeCode] + // " "); data.incrementPosition(32); serverTypeList.add(allHandlers.getHandlerForFormat(typeCode)); } // System.out.println(); send_null(CLIPRDR_FORMAT_ACK, CLIPRDR_RESPONSE); currentHandler = serverTypeList.getFirst(); if (currentHandler != null) request_clipboard_data(currentHandler.preferredFormat()); } void handle_data_request(RdpPacket data) throws RdesktopException, IOException, CryptoException { int format = data.getLittleEndian32(); Transferable clipData = clipboard.getContents(this); byte[] outData = null; TypeHandler outputHandler = allHandlers.getHandlerForFormat(format); if (outputHandler != null) { outputHandler.send_data(clipData, this); // outData = outputHandler.fromTransferable(clipData); // if(outData != null){ // send_data(outData,outData.length); // return; // } // else System.out.println("Clipboard data to send == null!"); } // this.send_null(CLIPRDR_DATA_RESPONSE,CLIPRDR_ERROR); } void handle_data_response(RdpPacket data, int length) { // if(currentHandler != // null)clipboard.setContents(currentHandler.handleData(data, // length),this); // currentHandler = null; if (currentHandler != null) currentHandler.handleData(data, length, this); currentHandler = null; } void request_clipboard_data(int formatcode) throws RdesktopException, IOException, CryptoException { RdpPacket_Localised s = Common.secure.init( Constants.encryption ? Secure.SEC_ENCRYPT : 0, 24); s.setLittleEndian32(16); // length int flags = VChannels.CHANNEL_FLAG_FIRST | VChannels.CHANNEL_FLAG_LAST; if ((this.flags() & VChannels.CHANNEL_OPTION_SHOW_PROTOCOL) != 0) flags |= VChannels.CHANNEL_FLAG_SHOW_PROTOCOL; s.setLittleEndian32(flags); s.setLittleEndian16(CLIPRDR_DATA_REQUEST); s.setLittleEndian16(CLIPRDR_REQUEST); s.setLittleEndian32(4); // Remaining length s.setLittleEndian32(formatcode); s.setLittleEndian32(0); // Unknown. Garbage pad? s.markEnd(); Common.secure.send_to_channel(s, Constants.encryption ? Secure.SEC_ENCRYPT : 0, this.mcs_id()); } public void send_data(byte[] data, int length) { CommunicationMonitor.lock(this); RdpPacket_Localised all = new RdpPacket_Localised(12 + length); all.setLittleEndian16(CLIPRDR_DATA_RESPONSE); all.setLittleEndian16(CLIPRDR_RESPONSE); all.setLittleEndian32(length + 4); // don't know why, but we need to add // between 1 and 4 to the length, // otherwise the server cliprdr thread hangs all.copyFromByteArray(data, 0, all.getPosition(), length); all.incrementPosition(length); all.setLittleEndian32(0); try { this.send_packet(all); } catch (RdesktopException e) { System.err.println(e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.err.println(e.getMessage()); e.printStackTrace(); } catch (CryptoException e) { System.err.println(e.getMessage()); e.printStackTrace(); } CommunicationMonitor.unlock(this); } /* * FocusListener methods */ public void focusGained(FocusEvent arg0) { // synchronise the clipboard types here, so the server knows what's // available if (Options.use_rdp5) { try { send_format_announce(); } catch (RdesktopException e) { } catch (IOException e) { } catch (CryptoException e) { } } } public void focusLost(FocusEvent arg0) { } /* * Support methods */ private void reset_bool(boolean[] x) { for (int i = 0; i < x.length; i++) x[i] = false; } /* * ClipboardOwner methods */ public void lostOwnership(Clipboard arg0, Transferable arg1) { logger.debug("Lost clipboard ownership"); } public void copyToClipboard(Transferable t) { clipboard.setContents(t, this); } }