/* for image data */ static XSelectionRequestEvent g_saved_selection_req_event;
/* xserver maximum request size in bytes */ staticint g_incr_max_req_size = 0;
/* server to client, pasting from linux app to mstsc */ struct clip_s2c g_clip_s2c; /* client to server, pasting from mstsc to linux app */ struct clip_c2s g_clip_c2s;
/* default version and flags */ staticint g_cliprdr_version = CB_CAPS_VERSION_2; staticint g_cliprdr_flags = CB_USE_LONG_FORMAT_NAMES |
CB_STREAM_FILECLIP_ENABLED |
CB_FILECLIP_NO_FILE_PATHS;
/* from client to server */ /* last received CLIPRDR_FORMAT_LIST(CLIPRDR_FORMAT_ANNOUNCE) */ staticint g_formatIds[16]; staticint g_num_formatIds = 0;
/* Format ID assigned to "FileGroupDescriptorW" by the client */ staticint g_file_group_descriptor_format_id = -1;
/*****************************************************************************/ staticchar *
get_atom_text(Atom atom)
{ char *name; int failed;
failed = 0; /* sanity check */ if ((atom < 1) || (atom > 512))
{
failed = 1;
} if (!failed)
{
name = XGetAtomName(g_display, atom); if (name == 0)
{
failed = 1;
}
} if (failed)
{
g_snprintf(g_last_atom_name, 255, "unknown atom 0x%8.8x", (int)atom); return g_last_atom_name;
}
g_strncpy(g_last_atom_name, name, 255);
XFree(name); return g_last_atom_name;
}
/*****************************************************************************/ /* this is one way to get the current time from the x server */ static Time
clipboard_get_server_time(void)
{
XEvent xevent; unsignedchar no_text[4];
/* wait for PropertyNotify */ do
{
XMaskEvent(g_display, PropertyChangeMask, &xevent);
} while (xevent.type != PropertyNotify);
return xevent.xproperty.time;
}
/*****************************************************************************/ staticint
clipboard_find_format_id(int format_id)
{ int index;
for (index = 0; index < g_num_formatIds; index++)
{ if (g_formatIds[index] == format_id)
{ return index;
}
} return -1;
}
/*****************************************************************************/ /* returns error */ int
clipboard_init(void)
{ struct stream *s; int size; int rv; int input_mask; int dummy; int ver_maj; int ver_min;
Status st;
/*****************************************************************************/ staticint
clipboard_send_data_response_for_image(constchar *data, int data_size)
{ struct stream *s; int size; int rv;
/*****************************************************************************/ staticint
clipboard_send_data_response_for_text(constchar *data, int data_size)
{ struct stream *s; int size; int rv; int num_words;
/*****************************************************************************/ staticint
clipboard_provide_selection(XSelectionRequestEvent *req, Atom type, int format, char *data, int length)
{
XEvent xev; int bytes;
/*****************************************************************************/ /* sent by client or server when its local system clipboard is updatedwithnewclipboarddata;containsClipboardFormatID
and name pairs of new Clipboard Formats on the clipboard. */ staticint
clipboard_process_format_announce(struct stream *s, int clip_msg_status, int clip_msg_len)
{ int formatId; int bytes; char desc[256];
/* format id for file copy copy is dynamic and announced by the
* client */ if (g_strcmp(desc, "FileGroupDescriptorW") == 0)
{
g_file_group_descriptor_format_id = formatId;
}
}
/*****************************************************************************/ /* response to CB_FORMAT_LIST; used to indicate whether
processing of the Format List PDU was successful */ staticint
clipboard_process_format_ack(struct stream *s, int clip_msg_status, int clip_msg_len)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_process_format_ack: CLIPRDR_FORMAT_ACK");
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_process_format_ack:"); return0;
}
/*****************************************************************************/ staticint
clipboard_send_data_response_failed(void)
{ struct stream *s; int size; int rv;
/*****************************************************************************/ /* sent from server to client *sentbyrecipientofCB_FORMAT_LIST;usedtorequestdataforone
* of the formats that was listed in CB_FORMAT_LIST */ staticint
clipboard_process_data_request(struct stream *s, int clip_msg_status, int clip_msg_len)
{ int requestedFormatId;
/**************************************************************************//**
* Process a CB_FORMAT_DATA_RESPONSE for an X client requesting an image
*
* @param s Stream containing CLIPRDR_FILELIST ([MS-RDPECLIP])
* @param clip_msg_status msgFlags from Clipboard PDU Header
* @param clip_msg_len dataLen from Clipboard PDU Header
*
* @return Status
*/ staticint
clipboard_process_data_response_for_image(struct stream *s, int clip_msg_status, int clip_msg_len)
{
XSelectionRequestEvent *lxev; int len; struct stream *bmp_hs;
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_process_data_response_for_image: " "CLIPRDR_DATA_RESPONSE_FOR_IMAGE");
lxev = &g_saved_selection_req_event;
len = (int)(s->end - s->p); if (len < 1)
{ return0;
} if (g_clip_c2s.type != g_image_bmp_atom)
{ return0;
}
/**************************************************************************//**
* Process a CB_FORMAT_DATA_RESPONSE for an X client requesting a file list
*
* @param s Stream containing CLIPRDR_FILELIST ([MS-RDPECLIP])
* @param clip_msg_status msgFlags from Clipboard PDU Header
* @param clip_msg_len dataLen from Clipboard PDU Header
*
* @return Status
*/ staticint
clipboard_process_data_response_for_file(struct stream *s, int clip_msg_status, int clip_msg_len)
{
XSelectionRequestEvent *lxev; int rv = 0;
/**************************************************************************//**
* Process a CB_FORMAT_DATA_RESPONSE for an X client requesting text
*
* @param s Stream containing CLIPRDR_FILELIST ([MS-RDPECLIP])
* @param clip_msg_status msgFlags from Clipboard PDU Header
* @param clip_msg_len dataLen from Clipboard PDU Header
*
* @return Status
*/ staticint
clipboard_process_data_response_for_text(struct stream *s, int clip_msg_status, int clip_msg_len)
{
XSelectionRequestEvent *lxev = &g_saved_selection_req_event; unsignedint byte_count;
/* Get the buffer size we need */
byte_count = in_utf16_le_terminated_as_utf8_length(s);
g_free(g_clip_c2s.data);
g_clip_c2s.total_bytes = 0; if ((g_clip_c2s.data = (char *)g_malloc(byte_count, 0)) == NULL)
{
LOG(LOG_LEVEL_ERROR, "Can't allocate %u bytes for text clip response",
byte_count);
clipboard_refuse_selection(lxev);
} else
{ /* Re-parse the data into the allocated buffer */
in_utf16_le_terminated_as_utf8(s, g_clip_c2s.data, byte_count);
--byte_count; /* Ignore the terminator at the end */
/*****************************************************************************/ /* client to server */ /* sent as a reply to CB_FORMAT_DATA_REQUEST; used to indicate whether processingoftheCB_FORMAT_DATA_REQUESTwassuccessful;ifprocessingwas successful,CB_FORMAT_DATA_RESPONSEincludescontentsofrequested
clipboard data. */ /*****************************************************************************/ staticint
clipboard_process_data_response(struct stream *s, int clip_msg_status, int clip_msg_len)
{ int rv = 0;
if ((clip_msg_status & CB_RESPONSE_FAIL) != 0)
{ /* Requested data was not returned from the client. Most likely *theclienthaslosttheselectionbetweenannouncingitand
* responding to our request */
clipboard_refuse_selection(lxev);
} elseif ((clip_msg_status & CB_RESPONSE_OK) == 0)
{ /* One of CB_RESPONSE_FAIL or CB_RESPONSE_OK MUST be set in
* a CLIPRDR_FORMAT_DATA_RESPONSE msg */
LOG(LOG_LEVEL_ERROR, "CLIPRDR_FORMAT_DATA_RESPONSE is badly formed");
clipboard_refuse_selection(lxev);
} elseif (g_clip_c2s.xrdp_clip_type == XRDP_CB_BITMAP)
{
clipboard_process_data_response_for_image(s, clip_msg_status,
clip_msg_len);
} elseif (g_clip_c2s.xrdp_clip_type == XRDP_CB_FILE)
{
clipboard_process_data_response_for_file(s, clip_msg_status,
clip_msg_len);
} else
{
clipboard_process_data_response_for_text(s, clip_msg_status,
clip_msg_len);
} return rv;
}
/*****************************************************************************/ staticint
clipboard_process_clip_caps(struct stream *s, int clip_msg_status, int clip_msg_len)
{ int cCapabilitiesSets; int capabilitySetType; int lengthCapability; int index; int version; int flags; char *holdp;
/*****************************************************************************/ int
clipboard_data_in(struct stream *s, int chan_id, int chan_flags, int length, int total_length)
{ int clip_msg_id; int clip_msg_len; int clip_msg_status; int rv; struct stream *ls;
if (!g_clip_up)
{
LOG_DEVEL(LOG_LEVEL_ERROR, "aborting clipboard_data_in - clipboard has not " "been initialized"); /* we return 0 here to indicate no protocol problem occurred */ return0;
}
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_data_in: %d", clip_msg_id); switch (clip_msg_id)
{ /* sent by client or server when its local system clipboard is */ /* updated with new clipboard data; contains Clipboard Format ID */ /* and name pairs of new Clipboard Formats on the clipboard. */ case CB_FORMAT_LIST: /* 2 CLIPRDR_FORMAT_ANNOUNCE */
rv = clipboard_process_format_announce(ls, clip_msg_status,
clip_msg_len); break; /* response to CB_FORMAT_LIST; used to indicate whether */ /* processing of the Format List PDU was successful */ case CB_FORMAT_LIST_RESPONSE: /* 3 CLIPRDR_FORMAT_ACK */
rv = clipboard_process_format_ack(ls, clip_msg_status,
clip_msg_len); break; /* sent by recipient of CB_FORMAT_LIST; used to request data for one */ /* of the formats that was listed in CB_FORMAT_LIST */ case CB_FORMAT_DATA_REQUEST: /* 4 CLIPRDR_DATA_REQUEST */
rv = clipboard_process_data_request(ls, clip_msg_status,
clip_msg_len); break; /* sent as a reply to CB_FORMAT_DATA_REQUEST; used to indicate */ /* whether processing of the CB_FORMAT_DATA_REQUEST was */ /* successful; if processing was successful, */ /* CB_FORMAT_DATA_RESPONSE includes contents of requested */ /* clipboard data. */ case CB_FORMAT_DATA_RESPONSE: /* 5 CLIPRDR_DATA_RESPONSE */
rv = clipboard_process_data_response(ls, clip_msg_status,
clip_msg_len); break; case CB_CLIP_CAPS: /* 7 */
rv = clipboard_process_clip_caps(ls, clip_msg_status,
clip_msg_len); break; case CB_FILECONTENTS_REQUEST: /* 8 */
rv = clipboard_process_file_request(ls, clip_msg_status,
clip_msg_len); break; case CB_FILECONTENTS_RESPONSE: /* 9 */
rv = clipboard_process_file_response(ls, clip_msg_status,
clip_msg_len); break; default:
LOG_DEVEL(LOG_LEVEL_ERROR, "clipboard_data_in: unknown clip_msg_id %d", clip_msg_id); break;
}
XFlush(g_display); return rv;
}
/*****************************************************************************/ /* this happens when a new app copies something to the clipboard 'CLIPBOARD'Atom typedefstruct { inttype; unsignedlongserial; Boolsend_event; Display*display; Windowwindow; intsubtype; Windowowner; Atomselection; Timetimestamp; Timeselection_timestamp;
} XFixesSelectionNotifyEvent; */ staticint
clipboard_event_selection_owner_notify(XEvent *xevent)
{
XFixesSelectionNotifyEvent *lxevent;
g_got_selection = 0; if (lxevent->owner != 0) /* nil owner comes when selection */
{ /* window is closed */
XConvertSelection(g_display, g_clipboard_atom, g_targets_atom,
g_clip_property_atom, g_wnd, lxevent->timestamp);
} return0;
}
/*****************************************************************************/ /* returns error
get a window property from wnd */ staticint
clipboard_get_window_property(Window wnd, Atom prop, Atom *type, int *fmt, int *n_items, char **xdata, int *xdata_size)
{ int lfmt; int lxdata_size; unsignedlong ln_items; unsignedlong llen_after;
tui8 *lxdata;
Atom ltype;
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_get_window_property:");
LOG_DEVEL(LOG_LEVEL_DEBUG, " prop %ld name %s", prop, get_atom_text(prop));
lxdata = 0;
ltype = 0;
XGetWindowProperty(g_display, wnd, prop, 0, 0, 0,
AnyPropertyType, <ype, &lfmt, &ln_items,
&llen_after, &lxdata); if (lxdata != 0)
{
XFree(lxdata);
} if (ltype == 0)
{ /* XGetWindowProperty failed */ return4;
} if (llen_after < 1)
{ /* no data, ok */ return0;
}
lxdata = 0;
ltype = 0;
XGetWindowProperty(g_display, wnd, prop, 0, (llen_after + 3) / 4, 0,
AnyPropertyType, <ype, &lfmt, &ln_items,
&llen_after, &lxdata); if (ltype == 0)
{ /* XGetWindowProperty failed */ if (lxdata != 0)
{
XFree(lxdata);
} return1;
}
lxdata_size = FORMAT_TO_BYTES(lfmt);
lxdata_size *= ln_items; if (lxdata_size < 1)
{ /* should not happen */ if (lxdata != 0)
{
XFree(lxdata);
} return2;
} if (llen_after > 0)
{ /* should not happen */ if (lxdata != 0)
{
XFree(lxdata);
} return3;
} if (xdata != 0)
{
*xdata = (char *) g_malloc(lxdata_size, 0);
g_memcpy(*xdata, lxdata, lxdata_size);
} if (lxdata != 0)
{
XFree(lxdata);
} if (xdata_size != 0)
{
*xdata_size = lxdata_size;
} if (fmt != 0)
{
*fmt = (int)lfmt;
} if (n_items != 0)
{
*n_items = (int)ln_items;
} if (type != 0)
{
*type = ltype;
} return0;
}
/*****************************************************************************/ /* returns error processtheSelectionNotifyXevent,usesXSelectionEvent typedefstruct{ inttype;// SelectionNotify unsignedlongserial;// # of last request processed by server Boolsend_event;// true if this came from a SendEvent request Display*display;// Display the event was read from Windowrequestor; Atomselection; Atomtarget; Atomproperty;// atom or None Timetime;
} XSelectionEvent; */ staticint
clipboard_event_selection_notify(XEvent *xevent)
{
XSelectionEvent *lxevent; char *data; int data_size; int n_items; int fmt; int rv; int index; int got_string; int got_utf8; int got_bmp_image; int send_format_announce;
Atom got_file_atom;
Atom atom;
Atom *atoms;
Atom type;
if (got_file_atom != 0)
{ /* text/uri-list or x-special/gnome-copied-files */
if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_FILE)
{
LOG(LOG_LEVEL_DEBUG, "outbound clipboard(file) is restricted because of config");
} else
{
g_clip_s2c.type = got_file_atom;
g_clip_s2c.xrdp_clip_type = XRDP_CB_FILE;
g_clip_s2c.converted = 0;
g_clip_s2c.clip_time = lxevent->time;
send_format_announce = 1;
}
} elseif (got_utf8)
{
if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_TEXT)
{
LOG(LOG_LEVEL_DEBUG, "outbound clipboard(text) is restricted because of config");
} else
{
g_clip_s2c.type = g_utf8_atom;
g_clip_s2c.xrdp_clip_type = XRDP_CB_TEXT;
g_clip_s2c.converted = 0;
g_clip_s2c.clip_time = lxevent->time;
send_format_announce = 1;
}
} elseif (got_string)
{
/* *Inmostcases,whencopyingtext,TARGETSatomandUTF8_STRINGatomexists, *itmeansthatthiscodeblockwhichchecksSTRINGatommightnotbeneverexecuted *inrecentplatforms. *Useechofoo|xclip-selectionclipboard-noutf8toreproduceit.
*/ if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_TEXT)
{
LOG(LOG_LEVEL_DEBUG, "outbound clipboard(text) is restricted because of config");
} else
{
g_clip_s2c.type = XA_STRING;
g_clip_s2c.xrdp_clip_type = XRDP_CB_TEXT;
g_clip_s2c.converted = 0;
g_clip_s2c.clip_time = lxevent->time;
send_format_announce = 1;
}
} elseif (got_bmp_image)
{
if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_IMAGE)
{
LOG(LOG_LEVEL_DEBUG, "outbound clipboard(image) is restricted because of config");
} else
{
g_clip_s2c.type = g_image_bmp_atom;
g_clip_s2c.xrdp_clip_type = XRDP_CB_BITMAP;
g_clip_s2c.converted = 0;
g_clip_s2c.clip_time = lxevent->time;
send_format_announce = 1;
}
}
if (send_format_announce)
{ if (clipboard_send_format_announce(g_clip_s2c.xrdp_clip_type) != 0)
{
rv = 4;
}
}
g_free(data); return rv;
}
/*****************************************************************************/ /* returns error processtheSelectionRequestXevent,usesXSelectionRequestEvent typedefstruct{ inttype;// SelectionRequest unsignedlongserial;// # of last request processed by server Boolsend_event;// true if this came from a SendEvent request Display*display;// Display the event was read from Windowowner; Windowrequestor; Atomselection; Atomtarget; Atomproperty; Timetime;
} XSelectionRequestEvent; */ /* *WhenXGetWindowPropertyandXChangePropertytalkabout"format32"it *doesn'tmeana32bitvalue,butactuallyalong.So32means4byteson *a32bitmachineand8bytesona64machine
*/ staticint
clipboard_event_selection_request(XEvent *xevent)
{
XSelectionRequestEvent *lxev;
Atom atom_buf[10];
Atom type; int atom_count; int fmt; int n_items; int xdata_size; char *xdata;
if ((g_clip_c2s.type == lxev->target) && g_clip_c2s.converted)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: -------------------------------------------"); if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE)
{
LOG(LOG_LEVEL_DEBUG, "inbound clipboard x-special/gnome-copied-files converted is restricted because of config");
clipboard_refuse_selection(lxev); return0;
} else
{
clipboard_provide_selection_c2s(lxev, lxev->target); return0;
}
} if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE)
{
LOG(LOG_LEVEL_DEBUG, "inbound clipboard x-special/gnome-copied-files is restricted because of config");
clipboard_refuse_selection(lxev); return0;
} else
{
g_memcpy(&g_saved_selection_req_event, lxev, sizeof(g_saved_selection_req_event));
g_clip_c2s.type = g_file_atom2;
g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE;
clipboard_send_data_request(g_file_group_descriptor_format_id); return0;
}
} else
{
LOG(LOG_LEVEL_ERROR, "clipboard_event_selection_request: unknown " "target %s", get_atom_text(lxev->target));
}
clipboard_refuse_selection(lxev); return0;
}
/*****************************************************************************/ /* returns error processtheSelectionClearXevent,usesXSelectionClearEvent typedefstruct{ inttype;// SelectionClear unsignedlongserial;// # of last request processed by server Boolsend_event;// true if this came from a SendEvent request Display*display;// Display the event was read from Windowwindow; Atomselection; Timetime;
} XSelectionClearEvent; */ staticint
clipboard_event_selection_clear(XEvent *xevent)
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_clear:"); return0;
}
/*****************************************************************************/ /* returns error typedefstruct{ inttype;// PropertyNotify unsignedlongserial;// # of last request processed by server Boolsend_event;// true if this came from a SendEvent request Display*display;// Display the event was read from Windowwindow; Atomatom; Timetime; intstate;// PropertyNewValue or PropertyDelete
} XPropertyEvent; */ staticint
clipboard_event_property_notify(XEvent *xevent)
{
Atom actual_type_return; int actual_format_return; unsignedlong nitems_returned; unsignedlong bytes_left; unsignedchar *data; int rv; int format_in_bytes; int new_data_len; int data_bytes; char *cptr;
if (g_clip_c2s.incr_in_progress &&
(xevent->xproperty.window == g_clip_c2s.window) &&
(xevent->xproperty.atom == g_clip_c2s.property) &&
(xevent->xproperty.state == PropertyDelete))
{
LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_property_notify: INCR PropertyDelete"); /* this is used for when copying a large clipboard to the other app,
it will delete the property so we know to send the next one */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.