1
/*********************************************************
2
* Copyright (C) 2005 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
22
* Set of functions in guest side for copy/paste (both file and text).
23
* Currently there are 2 versions copy/paste. Version 1 only supports
24
* text copy/paste, and based on backdoor cmd. Version 2 supports both
25
* text and file copy/paste, and based on guestRPC.
27
* G->H Text Copy/Paste (version 1)
28
* --------------------
29
* When Ungrab, CopyPaste_RequestSelection got called, which try to get
30
* selection text and send to backdoor.
32
* H->G Text Copy/Paste (version 1)
33
* --------------------
34
* When grab, CopyPaste_GetBackdoorSelections got called, which first
35
* get host selection text, then claim as selection owner. If some app
36
* asks for selection, CopyPasteSelectionGetCB will reply with host
39
* G->H Copy/Paste (version 2)
40
* --------------------
41
* When Ungrab, host vmx will send out rpc command "copypaste.gh.data.get",
42
* which handler is CopyPasteRpcInGHSetDataCB. It first tries to get selection
43
* contents and sends it back as rpc result. For file transfer, host vmx
44
* will transfer files with hgFileCopy lib. Function CopyPasteGHFileListGetNext
45
* will handle the file list during transfer.
47
* H->G Copy/Paste (version 2)
48
* --------------------
49
* When grab, host vmx will send host selection content to guest with
50
* out rpc command "copypaste.hg.data.set", which handler is
51
* CopyPasteRpcInHGSetDataCB. It first keeps a copy then claim as selection
52
* owner. If some app asks for files, CopyPasteSelectionGetCB will ask host
53
* vmx to transfer files into a temp dir with rpc "copypaste.hgCopyFiles",
54
* then reply with host selection file list. For KDE and gnome the file list
55
* format is different. If someapp asks for text, CopyPasteSelectionGetCB
56
* will provide the data.
59
#include "vmwareuserInt.h"
66
#include "vm_assert.h"
70
#include "eventManager.h"
75
#include "cpNameUtil.h"
76
#include "guestInfoLib.h"
83
#include "vmware/guestrpc/tclodefs.h"
86
* Gtk 1.2 doesn't know about the CLIPBOARD selection, but that doesn't matter, we
87
* just create the atom we need directly in main().
89
#ifndef GDK_SELECTION_CLIPBOARD
90
GdkAtom GDK_SELECTION_CLIPBOARD;
93
#ifndef GDK_SELECTION_TYPE_TIMESTAMP
94
GdkAtom GDK_SELECTION_TYPE_TIMESTAMP;
97
#ifndef GDK_SELECTION_TYPE_UTF8_STRING
98
GdkAtom GDK_SELECTION_TYPE_UTF8_STRING;
101
typedef struct FCPGHState {
108
* Currently there are 2 versions copy/paste.
109
* Key points in copy/paste version 1:
110
* 1. Only text copy/paste
111
* 2. copy/paste is based on backdoor directly
113
* Key points in copy/paste version 2:
114
* 1. Support both file/text copy/paste
115
* 2. Both file/text copy/paste are based on guestRPC
117
static int32 gVmxCopyPasteVersion = 1;
120
* Getting a selection is an asyncronous event, so we have to keep track of both
121
* selections globablly in order to decide which one to use.
123
static Bool gWaitingOnGuestSelection = FALSE;
124
static char gGuestSelPrimaryBuf[MAX_SELECTION_BUFFER_LENGTH];
125
static char gGuestSelClipboardBuf[MAX_SELECTION_BUFFER_LENGTH];
126
static uint64 gGuestSelPrimaryTime = 0;
127
static uint64 gGuestSelClipboardTime = 0;
128
static char gHostClipboardBuf[MAX_SELECTION_BUFFER_LENGTH];
130
/* Guest->Host state. */
131
FCPGHState gFcpGHState;
132
/* RPC buffer for Guest->Host FCP. */
133
static char *gGHFCPRpcResultBuffer;
134
/* File list size for Guest->Host FCP. */
135
static size_t gGHFCPListSize;
136
static Bool gHGFCPPending;
137
/* Current selection is text or file list (for FCP). */
138
static Bool gHGIsClipboardFCP;
140
* Total file size in selection list. This is used to check if there is enough
141
* space in guest OS for host->guest file transfer.
143
static uint64 gHGFCPTotalSize;
145
static GdkAtom gFCPAtom[NR_FCP_TARGETS];
147
/* Host->guest file transfer status, used for sync between transfer and paste. */
148
int gHGFCPFileTransferStatus;
150
static char gFileRoot[DND_MAX_PATH];
151
static size_t gFileRootSize;
152
static Bool gIsOwner;
153
static VmTimeType gHGGetListTime;
156
* Forward Declarations
158
static INLINE void CopyPasteStateInit(void);
159
static void CopyPasteSelectionReceivedCB(GtkWidget *widget,
160
GtkSelectionData *selection_data,
162
static void CopyPasteSelectionGetCB(GtkWidget *widget,
163
GtkSelectionData *selection_data,
167
static gint CopyPasteSelectionClearCB(GtkWidget *widget,
168
GdkEventSelection *event,
171
static void CopyPasteSetBackdoorSelections(void);
172
static Bool CopyPasteRpcInGHSetDataCB(char const **result, size_t *resultLen,
173
const char *name, const char *args,
174
size_t argsSize,void *clientData);
175
static Bool CopyPasteRpcInHGSetDataCB(char const **result, size_t *resultLen,
176
const char *name, const char *args,
177
size_t argsSize,void *clientData);
179
static INLINE void CopyPasteGHFileListClear(void);
180
static INLINE void CopyPasteGHFileListSet(char *fileList, size_t fileListSize);
182
/* This struct is only used by CopyPasteSelectionRemoveTarget. */
183
struct SelectionTargetList {
190
*-----------------------------------------------------------------------------
192
* CopyPasteSelectionRemoveTarget --
194
* To remove a target from a selection target list. The reason to develop
195
* this function is that in gtk there is only gtk_selection_add_target to
196
* add supported target to selection list, but no function to remove one.
202
* If no more target, the selection list will be removed too.
204
*-----------------------------------------------------------------------------
208
CopyPasteSelectionRemoveTarget(GtkWidget *widget,
212
const char *selection_handler_key = "gtk-selection-handlers";
213
struct SelectionTargetList *targetList;
215
GList *selectionLists;
217
/* Get selection list. */
218
selectionLists = gtk_object_get_data(GTK_OBJECT (widget), selection_handler_key);
219
tempList = selectionLists;
221
/* Enumerate the list to find the selection. */
222
targetList = tempList->data;
223
if (targetList->selection == selection) {
225
gtk_target_list_remove(targetList->list, target);
226
/* If no more target, remove selection from list. */
227
if (!targetList->list->list) {
228
/* Free target list. */
229
gtk_target_list_unref(targetList->list);
231
/* Remove and free selection node. */
232
selectionLists = g_list_remove_link(selectionLists, tempList);
233
g_list_free_1(tempList);
237
tempList = tempList->next;
239
/* Put new selection list back. */
240
gtk_object_set_data (GTK_OBJECT (widget), selection_handler_key, selectionLists);
245
*-----------------------------------------------------------------------------
247
* CopyPaste_RequestSelection --
249
* Request the guest's text clipboard (asynchronously), we'll give it to
250
* the host when the request completes. For version 1 guest->host text
257
* The owner of the clipboard will get a request from our application.
259
*-----------------------------------------------------------------------------
263
CopyPaste_RequestSelection(void)
265
if (gVmxCopyPasteVersion > 1) {
270
* Ask for both the PRIMARY and CLIPBOARD selections.
272
gGuestSelPrimaryBuf[0] = '\0';
273
gGuestSelClipboardBuf[0] = '\0';
275
/* Only send out request if we are not the owner. */
277
/* Try to get utf8 text from primary and clipboard. */
278
gWaitingOnGuestSelection = TRUE;
279
gtk_selection_convert(gUserMainWidget,
280
GDK_SELECTION_PRIMARY,
281
GDK_SELECTION_TYPE_UTF8_STRING,
283
while (gWaitingOnGuestSelection) gtk_main_iteration();
285
gWaitingOnGuestSelection = TRUE;
286
gtk_selection_convert(gUserMainWidget,
287
GDK_SELECTION_CLIPBOARD,
288
GDK_SELECTION_TYPE_UTF8_STRING,
290
while (gWaitingOnGuestSelection) gtk_main_iteration();
292
if (gGuestSelPrimaryBuf[0] == '\0' && gGuestSelClipboardBuf[0] == '\0') {
294
* If we cannot get utf8 text, try to get localized text from primary
297
gWaitingOnGuestSelection = TRUE;
298
gtk_selection_convert(gUserMainWidget,
299
GDK_SELECTION_PRIMARY,
300
GDK_SELECTION_TYPE_STRING,
302
while (gWaitingOnGuestSelection) gtk_main_iteration();
304
gWaitingOnGuestSelection = TRUE;
305
gtk_selection_convert(gUserMainWidget,
306
GDK_SELECTION_CLIPBOARD,
307
GDK_SELECTION_TYPE_STRING,
309
while (gWaitingOnGuestSelection) gtk_main_iteration();
312
/* Send text to host. */
313
Debug("CopyPaste_RequestSelection: Prim is [%s], Clip is [%s]\n",
314
gGuestSelPrimaryBuf, gGuestSelClipboardBuf);
315
CopyPasteSetBackdoorSelections();
320
*-----------------------------------------------------------------------------
322
* CopyPasteSelectionReceivedCB --
324
* Callback for the gtk signal "selection_recieved".
325
* Called because we previously requested a copy/paste selection and
326
* finally got results of that asynchronous operation. After some basic
327
* sanity checks, send the result (in selection_data) thru the backdoor
328
* (version 1) or guestRPC (version 2) so the vmx can copy it to host
331
* We made several requests for selections, the string (actual data) and
332
* file list for each of PRIMARY and CLIPBOARD selections. So this funtion
333
* will get called several times, once for each request.
335
* For guest->host copy/paste (both text and file).
343
*-----------------------------------------------------------------------------
347
CopyPasteSelectionReceivedCB(GtkWidget *widget, // IN: unused
348
GtkSelectionData *selection_data, // IN: requested data
349
gpointer data) // IN: unused
352
char *utf8Str = NULL;
356
if ((widget == NULL) || (selection_data == NULL)) {
357
Debug("CopyPasteSelectionReceivedCB: Error, widget or selection_data is invalid\n");
361
if (selection_data->length < 0) {
362
Debug("CopyPasteSelectionReceivedCB: Error, length less than 0\n");
366
/* Try to get clipboard or selection timestamp. */
367
if (selection_data->target == GDK_SELECTION_TYPE_TIMESTAMP) {
368
if (selection_data->selection == GDK_SELECTION_PRIMARY) {
369
if (selection_data->length == 4) {
370
gGuestSelPrimaryTime = *(uint32 *)selection_data->data;
371
Debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n",
372
gGuestSelPrimaryTime);
373
} else if (selection_data->length == 8) {
374
gGuestSelPrimaryTime = *(uint64 *)selection_data->data;
375
Debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n",
376
gGuestSelPrimaryTime);
378
Debug("CopyPasteSelectionReceivedCB: Unknown pri time. Size %d\n",
379
selection_data->length);
382
if (selection_data->selection == GDK_SELECTION_CLIPBOARD) {
383
if (selection_data->length == 4) {
384
gGuestSelClipboardTime = *(uint32 *)selection_data->data;
385
Debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n",
386
gGuestSelClipboardTime);
387
} else if (selection_data->length == 8) {
388
gGuestSelClipboardTime = *(uint64 *)selection_data->data;
389
Debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n",
390
gGuestSelClipboardTime);
392
Debug("CopyPasteSelectionReceivedCB: Unknown clip time. Size %d\n",
393
selection_data->length);
399
if (selection_data->selection == GDK_SELECTION_PRIMARY) {
400
target = gGuestSelPrimaryBuf;
401
} else if (selection_data->selection == GDK_SELECTION_CLIPBOARD) {
402
target = gGuestSelClipboardBuf;
407
utf8Str = selection_data->data;
408
len = strlen(selection_data->data);
410
if (selection_data->target != GDK_SELECTION_TYPE_STRING &&
411
selection_data->target != GDK_SELECTION_TYPE_UTF8_STRING) {
412
/* It is a file list. */
413
if (len >= MAX_SELECTION_BUFFER_LENGTH - 1) {
414
Warning("CopyPasteSelectionReceivedCB file list too long\n");
416
memcpy(target, selection_data->data, len + 1);
422
* If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code
423
* set. Convert to utf8 before send to vmx.
425
if (selection_data->target == GDK_SELECTION_TYPE_STRING &&
426
!CodeSet_CurrentToUtf8(selection_data->data,
427
selection_data->length,
430
Debug("CopyPasteSelectionReceivedCB: Couldn't convert to utf8 code set\n");
431
gWaitingOnGuestSelection = FALSE;
436
* String in backdoor communication is 4 bytes by 4 bytes, so the len
437
* should be aligned to 4;
439
aligned_len = (len + 4) & ~3;
440
if (aligned_len >= MAX_SELECTION_BUFFER_LENGTH) {
441
/* With alignment, len is still possible to be less than max. */
442
if (len < (MAX_SELECTION_BUFFER_LENGTH - 1)) {
443
memcpy(target, utf8Str, len + 1);
445
memcpy(target, utf8Str, MAX_SELECTION_BUFFER_LENGTH - 1);
446
target[MAX_SELECTION_BUFFER_LENGTH - 1] ='\0';
449
memcpy(target, utf8Str, len + 1);
453
if (selection_data->target == GDK_SELECTION_TYPE_STRING) {
456
gWaitingOnGuestSelection = FALSE;
461
*-----------------------------------------------------------------------------
463
* CopyPasteSelectionGetCB --
465
* Callback for the gtk signal "selection_get".
466
* This is called when some other app requests the copy/paste selection,
467
* probably because we declare oursleves the selection owner on mouse
468
* grab. In text copy/paste case, we simply respond with contents of
469
* gHostClipboardBuf, which should have been set on mouse grab. In file
470
* copy/paste case, send file transfer request to host vmx, then return
471
* file list with right format according to different request.
472
* For host->guest copy/paste (both text and file).
478
* An X message is sent to the requesting app containing the data, it
479
* will likely act on it in some way. In FCP case, may first start a
480
* host->guest file transfer. Add block if blocking driver is available,
481
* otherwise wait till file copy done.
483
*-----------------------------------------------------------------------------
487
CopyPasteSelectionGetCB(GtkWidget *widget, // IN: unused
488
GtkSelectionData *selection_data, // IN: requested type
489
// OUT:the data to be sent
490
guint info, // IN: unused
491
guint time_stamp, // IN: unsued
492
gpointer data) // IN: unused
494
const char *begin = NULL;
495
const char *end = NULL;
496
const char *next = NULL;
497
const char *pre = NULL;
498
const char *post = NULL;
504
Bool blockAdded = FALSE;
505
Bool gnomeFCP = FALSE;
508
if ((widget == NULL) || (selection_data == NULL)) {
509
Debug("CopyPasteSelectionGetCB: Error, widget or selection_data is invalid\n");
513
/* If it is text copy paste, return gHostClipboardBuf. */
514
if (GDK_SELECTION_TYPE_STRING == selection_data->target ||
515
GDK_SELECTION_TYPE_UTF8_STRING == selection_data->target) {
516
char *outBuf = gHostClipboardBuf;
517
size_t len = strlen(gHostClipboardBuf);
520
* If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code
521
* set. Convert from utf8 to local one.
523
if (GDK_SELECTION_TYPE_STRING == selection_data->target &&
524
!CodeSet_Utf8ToCurrent(gHostClipboardBuf,
525
strlen(gHostClipboardBuf),
528
Debug("CopyPasteSelectionGetCB: can not convert to current codeset\n");
532
gtk_selection_data_set(selection_data, selection_data->target, 8,
534
Debug("CopyPasteSelectionGetCB: Set text [%s]\n", outBuf);
536
if (GDK_SELECTION_TYPE_STRING == selection_data->target) {
543
if (selection_data->target != gFCPAtom[FCP_TARGET_INFO_URI_LIST] &&
544
selection_data->target != gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES]) {
545
Debug("CopyPasteSelectionGetCB: Got unknown target\n");
549
if (!gHGIsClipboardFCP) {
550
Debug("CopyPasteSelectionGetCB: no file list available\n");
555
* KDE may ask for clipboard content right after clipboard owner changed,
556
* and cause unexpected HG file copy. So HG FCP will return nothing for 1
557
* second after switch from host OS to guest OS. Please refer to bug 301971.
559
Hostinfo_GetTimeOfDay(&curTime);
560
if (curTime - gHGGetListTime < FCP_COPY_DELAY) {
561
Debug("%s: waiting for delay\n", __FUNCTION__);
565
if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFER_NOT_YET) {
566
if (GuestInfo_GetAvailableDiskSpace(gFileRoot) < gHGFCPTotalSize) {
567
Debug("CopyPasteSelectionGetCB no enough space to copy file from host.\n");
570
/* Send host a rpc to start file transfer. */
571
if (!GuestApp_RpcSendOneCPName("copypaste.hg.copy.files", ' ',
572
gFileRoot, gFileRootSize)) {
573
Debug("CopyPasteSelectionGetCB: failed sending copypaste.hg.copy.files "
577
gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRING;
580
if (DnD_BlockIsReady(&gBlockCtrl)) {
581
/* Add a block on the staging directory for this command. */
582
if (gBlockCtrl.AddBlock(gBlockCtrl.fd, gFileRoot)) {
583
Debug("CopyPasteSelectionGetCB: add block [%s].\n", gFileRoot);
586
Warning("CopyPasteSelectionGetCB: Unable to add block [%s].\n", gFileRoot);
592
* If there is no blocking driver, wait here till file copy is done.
593
* 2 reasons to keep this:
594
* 1. If run vmware-user stand-alone as non-root, blocking driver can not
595
* be opened. Debug purpose only.
596
* 2. Other platforms (Solaris, FreeBSD, etc) may also use this code, and there
597
* is no blocking driver yet.
599
Debug("CopyPasteSelectionGetCB no blocking driver, waiting for "
600
"HG file copy done ...\n");
601
while (gHGFCPFileTransferStatus != FCP_FILE_TRANSFERRED) {
606
nr = EventManager_ProcessNext(gEventQueue, (uint64 *)&tv.tv_usec);
608
Debug("CopyPasteSelectionGetCB unexpected end of loop: returned "
609
"value is %d.\n", nr);
612
if (select(0, NULL, NULL, NULL, &tv) == -1) {
613
Debug("CopyPasteSelectionGetCB error in select (%s).\n",
619
Debug("CopyPasteSelectionGetCB file transfer done!\n");
622
/* Setup the format string components */
623
if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) {
624
Debug("CopyPasteSelectionGetCB Got uri_list request!\n");
625
pre = DND_URI_LIST_PRE_KDE;
626
preLen = sizeof DND_URI_LIST_PRE_KDE - 1;
627
post = DND_URI_LIST_POST;
628
postLen = sizeof DND_URI_LIST_POST - 1;
630
if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES]) {
631
Debug("CopyPasteSelectionGetCB Got gnome_copied request!\n");
632
pre = FCP_GNOME_LIST_PRE;
633
preLen = sizeof FCP_GNOME_LIST_PRE - 1;
634
post = FCP_GNOME_LIST_POST;
635
postLen = sizeof FCP_GNOME_LIST_POST - 1;
639
text = Util_SafeRealloc(text, textLen);
640
Str_Snprintf(text, 6, "copy\n");
644
Debug("CopyPasteSelectionGetCB: invalid drag target info\n");
650
* Set begin to first non-NUL character and end to last NUL character to
651
* prevent errors in calling CPName_GetComponent().
653
for(begin = gHostClipboardBuf; *begin == '\0'; begin++)
655
end = CPNameUtil_Strrchr(gHostClipboardBuf, gGHFCPListSize + 1, '\0');
658
/* Build up selection data */
659
while ((len = CPName_GetComponent(begin, end, &next)) != 0) {
660
const size_t origTextLen = textLen;
661
Bool freeBegin = FALSE;
664
Debug("CopyPasteSelectionGetCB: error getting next component\n");
672
* A URI list will expect the provided path to be escaped. If we cannot
673
* escape the path for some reason we just use the unescaped version and
674
* hope that it works.
676
if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) {
678
char *escapedComponent;
680
int bytesToEsc[256] = { 0, };
682
/* We escape the following characters based on RFC 1630. */
687
bytesToEsc['%'] = 1; /* Escape character */
689
/* Escape non-ASCII characters so we can pass UTF-8 filenames */
690
for (escIndex = 0x80; escIndex < 0x100; escIndex++) {
691
bytesToEsc[escIndex] = 1;
694
escapedComponent = Escape_Do('%', bytesToEsc, begin, len, &newLen);
695
if (escapedComponent) {
696
begin = escapedComponent;
703
* Append component. NUL terminator was accounted for by initializing
704
* textLen to one above.
706
textLen += preLen + len + postLen;
707
text = Util_SafeRealloc(text, textLen);
710
* Bug 143147: Gnome FCP does not like the trailing newlines. We don't
711
* have this problem for targets that ask for URI lists. So we don't see
713
* - KDE which asks for URI lists during FCP
714
* - DnD in both Gnome and KDE since they ask for URI lists.
716
* This is a problem only for Gnome FCP which expects a specially
717
* formatted 'copy' command string containing the file list which it then
718
* converts into a URI list internally.
720
Str_Snprintf(text + origTextLen - 1,
721
textLen - origTextLen + 1,
722
"%s%s%s", pre, begin, gnomeFCP && next == end ? "" : post);
728
/* Iterate to next component */
733
* Send out the data using the selection system. When sending a string, GTK will
734
* ensure that a null terminating byte is added to the end so we do not need to
735
* add it. GTK also copies the data so the original will never be modified.
737
Debug("CopyPasteSelectionGetCB: set file list [%s]\n", text);
738
gtk_selection_data_set(selection_data, selection_data->target,
739
8, /* 8 bits per character. */
746
*-----------------------------------------------------------------------------
748
* CopyPasteSelectionClearCB --
750
* Callback for the gtk signal "selection_clear".
758
*-----------------------------------------------------------------------------
762
CopyPasteSelectionClearCB(GtkWidget *widget, // IN: unused
763
GdkEventSelection *event, // IN: unused
764
gpointer data) // IN: unused
766
Debug("CopyPasteSelectionClearCB got clear signal\n");
773
*-----------------------------------------------------------------------------
775
* CopyPasteSetBackdoorSelections --
777
* Set the clipboard one of two ways, the old way or the new way.
778
* The old way uses GuestApp_SetSel and there's only one selection.
779
* Set backdoor selection with either primary selection or clipboard.
780
* The primary selection is the first priority, then clipboard.
781
* If both unavailable, set backdoor selection length to be 0.
782
* This will be used by older VMXs or VMXs on Windows hosts (which
783
* has only one clipboard). Doing this gives us backwards
786
* The new way uses new sets both PRIMARY and CLIPBOARD. Newer Linux
787
* VMXs will use these rather than the above method and have the two
788
* selections set separately.
790
* XXX: The "new way" doesn't exist yet, the vmx has no support for it.
796
* The VMX probably changes some string buffers.
798
*-----------------------------------------------------------------------------
802
CopyPasteSetBackdoorSelections(void)
811
primaryLen = strlen(gGuestSelPrimaryBuf);
812
clipboardLen = strlen(gGuestSelClipboardBuf);
816
* Send primary selection to backdoor if it exists.
818
p = (uint32 const *)gGuestSelPrimaryBuf;
819
} else if (clipboardLen) {
821
* Otherwise send clipboard to backdoor if it exists.
823
p = (uint32 const *)gGuestSelClipboardBuf;
826
* Neither selection is set
832
GuestApp_SetSelLength(0);
833
Debug("CopyPasteSetBackdoorSelections Set empty text.\n");
835
len = strlen((char *)p);
836
Debug("CopyPasteSetBackdoorSelections Set text [%s].\n", (char *)p);
837
aligned_len = (len + 4) & ~3;
839
/* Here long string should already be truncated. */
840
ASSERT(aligned_len <= MAX_SELECTION_BUFFER_LENGTH);
842
GuestApp_SetSelLength(len);
843
for (i = 0; i < len; i += 4, p++) {
844
GuestApp_SetNextPiece(*p);
851
*-----------------------------------------------------------------------------
853
* CopyPaste_GetBackdoorSelections --
855
* Get the clipboard "the old way".
856
* The old way uses GuestApp_SetSel and there's only one selection.
857
* We don't have to do anything for the "new way", since the host
858
* will just push PRIMARY and/or CLIPBOARD when they are available
861
* XXX: the "new way" isn't availble yet because the vmx doesn't
862
* implement separate clipboards. Even when it does this
863
* function will still exist for backward compatibility
866
* TRUE if selection length>=0, FLASE otherwise.
869
* This application becomes the selection owner for PRIMARY and/or
870
CLIPBOARD selections.
872
*-----------------------------------------------------------------------------
876
CopyPaste_GetBackdoorSelections(void)
881
if (gVmxCopyPasteVersion > 1) {
885
selLength = GuestApp_GetHostSelectionLen();
888
} else if (selLength > 0) {
889
memset(gHostClipboardBuf, 0, sizeof (gHostClipboardBuf));
890
GuestApp_GetHostSelection(selLength, gHostClipboardBuf);
891
Debug("CopyPaste_GetBackdoorSelections Get text [%s].\n", gHostClipboardBuf);
892
gtk_selection_owner_set(gUserMainWidget,
893
GDK_SELECTION_CLIPBOARD,
895
gtk_selection_owner_set(gUserMainWidget,
896
GDK_SELECTION_PRIMARY,
899
gHGIsClipboardFCP = FALSE;
900
for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) {
901
CopyPasteSelectionRemoveTarget(gUserMainWidget,
902
GDK_SELECTION_PRIMARY,
904
CopyPasteSelectionRemoveTarget(gUserMainWidget,
905
GDK_SELECTION_CLIPBOARD,
914
*-----------------------------------------------------------------------------
916
* CopyPasteRpcInGHSetDataCB --
918
* Handler function for the "copypaste.gh.data.get" RPC command. Host is
919
* asking for clipboard contents for guest->host copy/paste. If both primary
920
* selection and clipboard are empty, the empty list should also be sent back
921
* because Host should release clipboard owner.
923
* For Guest->Host copy/paste operations only.
926
* TRUE on success, FALSE on failure.
929
* The owner of the clipboard will get requests from our application.
931
*-----------------------------------------------------------------------------
935
CopyPasteRpcInGHSetDataCB(char const **result, // OUT
936
size_t *resultLen, // OUT
937
const char *name, // IN
938
const char *args, // IN
939
size_t argsSize, // Ignored
940
void *clientData) // Ignored
943
GdkAtom activeSelection = GDK_SELECTION_PRIMARY;
944
char *source = gGuestSelPrimaryBuf;
946
char *rpcBody = NULL;
947
size_t rpcBodySize = 0;
949
gGuestSelPrimaryBuf[0] = '\0';
950
gGuestSelClipboardBuf[0] = '\0';
953
Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n");
954
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
957
/* First check which one is newer, primary selection or clipboard. */
958
gGuestSelPrimaryTime = 0;
959
gGuestSelClipboardTime = 0;
961
gWaitingOnGuestSelection = TRUE;
962
gtk_selection_convert(gUserMainWidget,
963
GDK_SELECTION_PRIMARY,
964
GDK_SELECTION_TYPE_TIMESTAMP,
966
while (gWaitingOnGuestSelection) gtk_main_iteration();
968
gWaitingOnGuestSelection = TRUE;
969
gtk_selection_convert(gUserMainWidget,
970
GDK_SELECTION_CLIPBOARD,
971
GDK_SELECTION_TYPE_TIMESTAMP,
973
while (gWaitingOnGuestSelection) gtk_main_iteration();
975
if (gGuestSelPrimaryTime < gGuestSelClipboardTime) {
976
activeSelection = GDK_SELECTION_CLIPBOARD;
977
source = gGuestSelClipboardBuf;
981
/* Check if it is file list in the active selection. */
982
for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) {
983
gWaitingOnGuestSelection = TRUE;
984
gtk_selection_convert(gUserMainWidget,
988
while (gWaitingOnGuestSelection) gtk_main_iteration();
989
if (source[0] != '\0') {
990
if (gVmxCopyPasteVersion < 2) {
991
/* Only vmx version greater than 2 support file copy/paste. */
992
Debug("CopyPasteRpcInGHSetDataCB invalid operation\n");
993
return RpcIn_SetRetVals(result, resultLen,
994
"invalid operation", FALSE);
1000
if (source[0] != '\0') {
1004
char *ghFileList = NULL;
1005
size_t ghFileListSize = 0;
1008
* In gnome, before file list there may be a extra line indicating it
1011
if (strncmp(source, "copy", 4) == 0) {
1014
if (strncmp(source, "cut", 3) == 0) {
1018
while (*source == '\n' || *source == '\r' || *source == ' ') {
1023
* Get the the full filenames and last components from the URI list. The
1024
* body of the RPC message will be these last components delimited with
1025
* NUL characters; the Guest->Host file list will be the full paths
1026
* delimited by NUL characters.
1028
while ((currName = DnD_UriListGetNextFile(source,
1031
size_t lastComponentSize;
1032
char *lastComponentStart;
1034
/* Append current filename to Guest->Host list */
1035
ghFileList = Util_SafeRealloc(ghFileList,
1036
ghFileListSize + currSize + 1);
1037
memcpy(ghFileList + ghFileListSize, currName, currSize);
1038
ghFileListSize += currSize;
1039
ghFileList[ghFileListSize] = '\0';
1042
/* Append last component to RPC body */
1043
lastComponentStart = CPNameUtil_Strrchr(currName, currSize, DIRSEPC);
1044
if (!lastComponentStart) {
1046
* This shouldn't happen since filenames are absolute, but handle
1047
* it as if the file name is the last component
1049
lastComponentStart = currName;
1051
/* Skip the last directory separator */
1052
lastComponentStart++;
1055
lastComponentSize = currName + currSize - lastComponentStart;
1056
rpcBody = Util_SafeRealloc(rpcBody, rpcBodySize + lastComponentSize + 1);
1057
memcpy(rpcBody + rpcBodySize, lastComponentStart, lastComponentSize);
1058
rpcBodySize += lastComponentSize;
1059
rpcBody[rpcBodySize] = '\0';
1065
if (!ghFileList || !rpcBody) {
1066
Warning("CopyPasteRpcInGHSetDataCB: no filenames retrieved "
1070
return RpcIn_SetRetVals(result, resultLen,
1071
"error retrieving file name", FALSE);
1074
/* Set the list of full paths */
1075
CopyPasteGHFileListSet(ghFileList, ghFileListSize);
1077
/* rpcBody (and its size) will always contain a trailing NUL character */
1079
Debug("CopyPasteRpcInGHSetDataCB: Sending: [%s] (%zu)\n",
1080
CPName_Print(rpcBody, rpcBodySize), rpcBodySize);
1082
Str_Sprintf(format, sizeof format, "%d ", CPFORMAT_FILELIST);
1083
*resultLen = rpcBodySize + strlen(format);
1085
free(gGHFCPRpcResultBuffer);
1086
gGHFCPRpcResultBuffer = Util_SafeCalloc(1, rpcBodySize + strlen(format));
1088
memcpy(gGHFCPRpcResultBuffer, format, strlen(format));
1089
memcpy(gGHFCPRpcResultBuffer + strlen(format), rpcBody, rpcBodySize);
1091
*result = gGHFCPRpcResultBuffer;
1094
/* Try to get utf8 text from active selection. */
1095
gWaitingOnGuestSelection = TRUE;
1096
gtk_selection_convert(gUserMainWidget,
1098
GDK_SELECTION_TYPE_UTF8_STRING,
1100
while (gWaitingOnGuestSelection) gtk_main_iteration();
1102
if (source[0] == '\0') {
1103
/* Try to get text from active selection. */
1104
gWaitingOnGuestSelection = TRUE;
1105
gtk_selection_convert(gUserMainWidget,
1107
GDK_SELECTION_TYPE_STRING,
1109
while (gWaitingOnGuestSelection) gtk_main_iteration();
1113
* With 'cut' operation OpenOffice will put data into clipboard but
1114
* set same timestamp for both clipboard and primary selection.
1115
* If primary timestamp is same as clipboard timestamp, we should try
1116
* clipboard again if primary selection is empty. For details please
1117
* refer to bug 300780.
1119
if (source[0] == '\0' &&
1120
gGuestSelPrimaryTime == gGuestSelClipboardTime &&
1121
gGuestSelPrimaryTime != 0) {
1122
gGuestSelPrimaryTime = 0;
1123
gGuestSelClipboardTime = 0;
1124
activeSelection = GDK_SELECTION_CLIPBOARD;
1125
source = gGuestSelClipboardBuf;
1129
if (source[0] != '\0') {
1130
free(gGHFCPRpcResultBuffer);
1132
gGHFCPRpcResultBuffer =
1133
Str_Asprintf(NULL, "%d %s", CPFORMAT_TEXT, source);
1135
if (!gGHFCPRpcResultBuffer) {
1136
Debug("CopyPasteRpcInGHSetDataCB failed to alloc memory.\n");
1137
return RpcIn_SetRetVals(result, resultLen,
1138
"error allocating memory", FALSE);
1141
*result = gGHFCPRpcResultBuffer;
1142
*resultLen = strlen(gGHFCPRpcResultBuffer);
1144
Debug("CopyPasteRpcInGHSetDataCB creating text: %s\n", source);
1148
/* Neither file list nor text is available, send empty list back. */
1149
Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n");
1150
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1156
*----------------------------------------------------------------------------
1158
* CopyPasteRpcInGHFinishCB --
1160
* For Guest->Host operations only.
1162
* Invoked when host side of copyPaste operation has finished.
1165
* TRUE on success, FALSE on failure.
1170
*----------------------------------------------------------------------------
1174
CopyPasteRpcInGHFinishCB(char const **result, // OUT
1175
size_t *resultLen, // OUT
1176
const char *name, // IN
1177
const char *args, // IN
1178
size_t argsSize, // Ignored
1179
void *clientData) // IN
1181
char *effect = NULL;
1182
unsigned int index = 0;
1184
gFcpGHState.fileListNext = gFcpGHState.fileList;
1186
effect = StrUtil_GetNextToken(&index, args, " ");
1188
Warning("CopyPasteRpcInGHFinishCB: no drop effect provided\n");
1189
return RpcIn_SetRetVals(result, resultLen,
1190
"drop effect not provided", FALSE);
1193
Debug("CopyPasteRpcInGHFinishCB got effect %s\n", effect);
1196
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1201
*----------------------------------------------------------------------------
1203
* CopyPasteGHFileListClear --
1205
* Clears existing Guest->Host file list, releasing any used resources.
1213
*----------------------------------------------------------------------------
1217
CopyPasteGHFileListClear(void)
1219
Debug("CopyPasteGHFileListClear: clearing G->H file list\n");
1220
if (gFcpGHState.fileList) {
1221
free(gFcpGHState.fileList);
1222
gFcpGHState.fileList = NULL;
1224
gFcpGHState.fileListSize = 0;
1225
gFcpGHState.fileListNext = NULL;
1230
*----------------------------------------------------------------------------
1232
* CopyPasteGHFileListSet --
1234
* Sets the Guest->Host file list that is accessed through
1235
* CopyPasteGHFileListGetNext().
1241
* Clears the existing Guest->Host file list if it exists.
1243
*----------------------------------------------------------------------------
1247
CopyPasteGHFileListSet(char *fileList, // IN: new Guest->Host file list
1248
size_t fileListSize) // IN: size of the provided list
1250
CopyPasteGHFileListClear();
1251
gFcpGHState.fileList = fileList;
1252
gFcpGHState.fileListSize = fileListSize;
1253
gFcpGHState.fileListNext = fileList;
1255
Debug("CopyPasteGHFileListSet: [%s] (%"FMTSZ"u)\n",
1256
CPName_Print(gFcpGHState.fileList, gFcpGHState.fileListSize),
1257
gFcpGHState.fileListSize);
1262
*----------------------------------------------------------------------------
1264
* CopyPasteGHFileListGetNext --
1266
* Retrieves the next file in the Guest->Host file list.
1268
* Note that this function may only be called after calling
1269
* CopyPasteGHFileListSet() and before calling CopyPasteGHFileListClear().
1272
* TRUE on success, FALSE on failure. If TRUE is returned, fileName is
1273
* given a pointer to the filename's location or NULL if there are no more
1274
* files, and fileNameSize is given the length of fileName.
1277
* The fileListNext value of the Guest->Host global state is updated.
1279
*----------------------------------------------------------------------------
1283
CopyPasteGHFileListGetNext(char **fileName, // OUT: fill with filename location
1284
size_t *fileNameSize) // OUT: fill with filename length
1290
ASSERT(gFcpGHState.fileList);
1291
ASSERT(gFcpGHState.fileListNext);
1292
ASSERT(gFcpGHState.fileListSize > 0);
1294
/* Ensure end is the last NUL character */
1295
end = CPNameUtil_Strrchr(gFcpGHState.fileList,
1296
gFcpGHState.fileListSize,
1300
/* Get the length of this filename and a pointer to the next one */
1301
len = CPName_GetComponent(gFcpGHState.fileListNext, end, &next);
1303
Warning("CopyPasteGHFileListGetNext: error retrieving next component\n");
1307
/* No more entries in the list */
1309
Debug("CopyPasteGHFileListGetNext: no more entries\n");
1312
gFcpGHState.fileListNext = gFcpGHState.fileList;
1316
Debug("CopyPasteGHFileListGetNext: returning [%s] (%d)\n",
1317
gFcpGHState.fileListNext, len);
1319
*fileName = gFcpGHState.fileListNext;
1320
*fileNameSize = len;
1321
gFcpGHState.fileListNext = (char *)next;
1327
*-----------------------------------------------------------------------------
1329
* CopyPasteHGSetData --
1331
* Host is sending text for copy/paste.
1333
* RPC command format:
1336
* 3. If text size > 0, then followed by text, otherwise nothing
1338
* For Host->Guest operations only.
1341
* TRUE on success, FALSE otherwise.
1346
*-----------------------------------------------------------------------------
1350
CopyPasteHGSetData(char const **result, // OUT
1351
size_t *resultLen, // OUT
1352
const char *args) // IN
1354
char *format = NULL;
1357
unsigned int index = 0;
1361
/* Parse value string. */
1362
format = StrUtil_GetNextToken(&index, args, " ");
1363
index++; /* Ignore leading space before data. */
1364
sSize = StrUtil_GetNextToken(&index, args, " ");
1366
if (!format || !sSize) {
1367
Debug("CopyPasteHGSetData failed to parse format & size\n");
1368
ret = RpcIn_SetRetVals(result, resultLen,
1369
"format and size is not completed", FALSE);
1373
textSize = atoi(sSize);
1374
gHostClipboardBuf[0] = '\0';
1377
if (textSize >= MAX_SELECTION_BUFFER_LENGTH) {
1378
textSize = MAX_SELECTION_BUFFER_LENGTH - 1;
1380
memcpy(gHostClipboardBuf, args + index, textSize);
1381
gHostClipboardBuf[textSize] = '\0';
1382
Debug("CopyPasteHGSetData: Set text [%s]\n", gHostClipboardBuf);
1385
gtk_selection_owner_set(gUserMainWidget,
1386
GDK_SELECTION_CLIPBOARD,
1388
gtk_selection_owner_set(gUserMainWidget,
1389
GDK_SELECTION_PRIMARY,
1392
gHGIsClipboardFCP = FALSE;
1394
/* We put text into selection, so remove file target types from list. */
1395
for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) {
1396
CopyPasteSelectionRemoveTarget(gUserMainWidget,
1397
GDK_SELECTION_PRIMARY,
1399
CopyPasteSelectionRemoveTarget(gUserMainWidget,
1400
GDK_SELECTION_CLIPBOARD,
1404
ret = RpcIn_SetRetVals(result, resultLen, "", TRUE);
1414
*-----------------------------------------------------------------------------
1416
* CopyPasteRpcInHGDataFinishCB --
1418
* For Host->Guest operations only.
1419
* Host has finished transferring copyPaste data to the guest. We do any
1420
* post H->G operation cleanup here, like picking a new file root.
1423
* TRUE on success, FALSE otherwise
1426
* Copied files will be deleted in error or cancel case.
1428
*-----------------------------------------------------------------------------
1432
CopyPasteRpcInHGDataFinishCB(char const **result, // OUT
1433
size_t *resultLen, // OUT
1434
const char *name, // IN
1435
const char *args, // IN
1436
size_t argsSize, // Ignored
1437
void *clientData) // IN: pointer to mainWnd
1439
unsigned int index = 0;
1442
Debug("CopyPasteRpcInHGDataFinishCB received copypaste data finish\n");
1444
state = StrUtil_GetNextToken(&index, args, " ");
1447
Debug("CopyPasteRpcInHGDataFinishCB failed to parse data state\n");
1448
return RpcIn_SetRetVals(result, resultLen,
1449
"must specify data finish state", FALSE);
1452
if (strcmp(state, "success") != 0) {
1453
Debug("CopyPasteRpcInHGDataFinishCB data transfer error\n");
1455
* Delete staging directory in error or cancel case, otherwise
1456
* target application may still try to get copied files because
1457
* the file list is provided right after adding block to staging
1460
File_DeleteDirectoryTree(gFileRoot);
1465
ASSERT(gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING);
1466
gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRED;
1468
if (DnD_BlockIsReady(&gBlockCtrl) &&
1469
!gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) {
1470
Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n",
1474
/* get new root dir for next FCP operation. */
1475
gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot);
1477
Debug("CopyPasteRpcInHGDataFinishCB create staging dir [%s]\n", gFileRoot);
1479
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1484
*-----------------------------------------------------------------------------
1486
* CopyPasteHGSetFileList --
1488
* Host is sending file list for FCP (file copy/paste).
1490
* RPC command format:
1492
* 2. Total size of all files in the list
1493
* 3. Size of file list string
1494
* 4. If list size > 0, then followed by file list, otherwise nothing
1496
* For Host->Guest FCP operations only.
1499
* TRUE on success, FALSE on failure.
1504
*-----------------------------------------------------------------------------
1508
CopyPasteHGSetFileList(char const **result, // OUT
1509
size_t *resultLen, // OUT
1510
const char *args) // IN
1512
char *format = NULL;
1514
char *sListSize = NULL;
1516
char *sTotalSize = NULL;
1517
char *stagingDirName = NULL;
1518
char mountDirName[DND_MAX_PATH];
1519
unsigned int index = 0;
1525
gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET;
1526
/* Parse value string. */
1527
format = StrUtil_GetNextToken(&index, args, " ");
1528
index++; /* Ignore leading space before data. */
1529
sTotalSize = StrUtil_GetNextToken(&index, args, " ");
1531
sListSize = StrUtil_GetNextToken(&index, args, " ");
1533
if (!format || !sTotalSize || !sListSize) {
1534
Debug("CopyPasteHGSetFileList failed to parse format & size\n");
1535
retStr = "format or size is not completed";
1539
listSize = atoi(sListSize);
1541
* Total file size in selection list. This is used to check if there is enough
1542
* space in guest OS for host->guest file transfer.
1544
gHGFCPTotalSize = atol(sTotalSize);
1546
if (listSize <= 0) {
1547
Debug("CopyPasteHGSetFileList: got empty list\n");
1548
gHostClipboardBuf[0] = '\0';
1555
* XXX Should do code set convertion here from utf8 to current for file list,
1556
* but right now should not do that. The reason is that the hgfs server
1557
* always puts utf8 file name into guest, which is not right if local guest
1558
* encoding is non-utf8. DnD has same problem.
1561
data = (char *)Util_SafeCalloc(1, listSize + 1);
1562
memcpy(data, args + index, listSize);
1563
data[listSize] = '\0';
1565
usingDnDBlock = DnD_BlockIsReady(&gBlockCtrl);
1566
if (usingDnDBlock) {
1568
* Here we take the last component of the actual file root, which is
1569
* a temporary directory for this DnD operation, and append it to the
1570
* mount point for vmblock. This is where we want the target application
1571
* to access the file since it will enable vmblock to block that
1572
* application's progress if necessary.
1574
stagingDirName = DnD_GetLastDirName(gFileRoot);
1575
if (!stagingDirName) {
1576
Debug("CopyPasteHGSetFileList: error construct stagingDirName\n");
1577
retStr = "error construct stagingDirName";
1580
if (strlen(gBlockCtrl.blockRoot) +
1581
(sizeof DIRSEPS - 1) * 2 + strlen(stagingDirName) >= sizeof mountDirName) {
1582
Debug("CopyPasteHGSetFileList: directory name too large.\n");
1583
retStr = "directory name too large";
1586
Str_Sprintf(mountDirName, sizeof mountDirName,
1587
"%s" DIRSEPS "%s" DIRSEPS, gBlockCtrl.blockRoot, stagingDirName);
1590
/* Add the file root to the relative paths received from host */
1591
if (!DnD_PrependFileRoot(usingDnDBlock ? mountDirName : gFileRoot,
1592
&data, &listSize)) {
1593
Debug("CopyPasteHGSetFileList: error prepending guest file root\n");
1594
retStr = "error prepending file root";
1598
if (listSize + 1 > sizeof gHostClipboardBuf) {
1599
Debug("CopyPasteHGSetFileList: data too large\n");
1600
retStr = "data too large";
1604
memcpy(gHostClipboardBuf, data, listSize + 1);
1605
gGHFCPListSize = listSize;
1606
gHGIsClipboardFCP = TRUE;
1607
Debug("CopyPasteHGSetFileList: get file list [%s] (%zu)\n",
1608
CPName_Print(gHostClipboardBuf, gGHFCPListSize), gGHFCPListSize);
1610
for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) {
1611
gtk_selection_add_target(gUserMainWidget, GDK_SELECTION_PRIMARY,
1612
gFCPAtom[iAtom], 0);
1613
gtk_selection_add_target(gUserMainWidget, GDK_SELECTION_CLIPBOARD,
1614
gFCPAtom[iAtom], 0);
1617
Debug("CopyPasteHGSetFileList: added targets\n");
1618
gtk_selection_owner_set(gUserMainWidget,
1619
GDK_SELECTION_CLIPBOARD,
1621
gtk_selection_owner_set(gUserMainWidget,
1622
GDK_SELECTION_PRIMARY,
1634
free(stagingDirName);
1635
Hostinfo_GetTimeOfDay(&gHGGetListTime);
1636
return RpcIn_SetRetVals(result, resultLen, retStr, ret);
1641
*-----------------------------------------------------------------------------
1643
* CopyPasteRpcInHGSetDataCB --
1645
* Host is sending data for copy/paste. The data can be text, file list, etc.
1647
* For Host->Guest operations only.
1650
* TRUE if success, FALSE otherwise.
1655
*-----------------------------------------------------------------------------
1659
CopyPasteRpcInHGSetDataCB(char const **result, // OUT
1660
size_t *resultLen, // OUT
1661
const char *name, // IN
1662
const char *args, // IN
1663
size_t argsSize, // Ignored
1664
void *clientData) // IN: ignored
1666
char *formatStr = NULL;
1667
DND_CPFORMAT format;
1670
unsigned int index = 0;
1672
if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) {
1673
return RpcIn_SetRetVals(result, resultLen,
1677
/* Parse value string. */
1678
formatStr = StrUtil_GetNextToken(&index, args, " ");
1679
index++; /* Ignore leading space before data. */
1682
Debug("CopyPasteTcloHGDataSet failed to parse format\n");
1683
return RpcIn_SetRetVals(result, resultLen,
1684
"format and size is not completed", FALSE);
1687
format = (DND_CPFORMAT)atoi(formatStr);
1692
ret = CopyPasteHGSetData(result, resultLen, args);
1694
case CPFORMAT_FILELIST:
1695
/* Only vmx version greater than 2 support file copy/paste. */
1696
if (gVmxCopyPasteVersion < 2) {
1697
Debug("CopyPasteRpcInHGSetDataCB invalid operation\n");
1698
return RpcIn_SetRetVals(result, resultLen,
1699
"invalid operation", FALSE);
1701
ret = CopyPasteHGSetFileList(result, resultLen, args);
1704
Debug("CopyPasteTcloHGDataSet unknown format\n");
1705
ret = RpcIn_SetRetVals(result, resultLen,
1706
"unknown format", FALSE);
1715
*----------------------------------------------------------------------------
1717
* CopyPasteRpcInGHGetNextFileCB --
1719
* For Guest->Host operations only.
1721
* Invoked when the host is compiling its list of files to copy from the
1722
* guest. Here we provide the path of the next file in our Guest->Host file
1723
* list in guest path format (for display purposes) and CPName format (for
1724
* file copy operation).
1727
* TRUE on success, FALSE on failure.
1730
* Iterator pointer within file list of GH state is iterated to next list
1731
* entry (through call to CopyPasteGHFileListGetNext()).
1733
*----------------------------------------------------------------------------
1737
CopyPasteRpcInGHGetNextFileCB(char const **result, // OUT
1738
size_t *resultLen, // OUT
1739
const char *name, // IN
1740
const char *args, // IN
1741
size_t argsSize, // Ignored
1742
void *clientData) // IN
1744
static char resultBuffer[DND_MAX_PATH];
1746
size_t fileNameSize;
1751
* Retrieve a pointer to the next filename and its size from the list stored
1752
* in the G->H DnD state. Note that fileName should not be free(3)d here
1753
* since an additional copy is not allocated.
1755
res = CopyPasteGHFileListGetNext(&fileName, &fileNameSize);
1758
Warning("CopyPasteRpcInGHGetNextFileCB: error retrieving file name\n");
1759
return RpcIn_SetRetVals(result, resultLen, "error getting file", FALSE);
1763
/* There are no more files to send */
1764
Debug("CopyPasteRpcInGHGetNextFileCB: reached end of Guest->Host file list\n");
1765
return RpcIn_SetRetVals(result, resultLen, "|end|", TRUE);
1768
if (fileNameSize + 1 + fileNameSize > sizeof resultBuffer) {
1769
Warning("CopyPasteRpcInGHGetNextFileCB: filename too large (%"FMTSZ"u)\n", fileNameSize);
1770
return RpcIn_SetRetVals(result, resultLen, "filename too large", FALSE);
1774
* Construct a reply message of the form:
1775
* <file name in guest format><NUL><filename in CPName format>
1777
memcpy(resultBuffer, fileName, fileNameSize);
1778
resultBuffer[fileNameSize] = '\0';
1780
cpNameSize = CPNameUtil_ConvertToRoot(fileName,
1781
sizeof resultBuffer - (fileNameSize + 1),
1782
resultBuffer + fileNameSize + 1);
1783
if (cpNameSize < 0) {
1784
Warning("CopyPasteRpcInGHGetNextFileCB: could not convert to CPName\n");
1785
return RpcIn_SetRetVals(result, resultLen,
1786
"error on CPName conversion", FALSE);
1789
/* Set manually because RpcIn_SetRetVals() assumes no NUL characters */
1790
*result = resultBuffer;
1791
*resultLen = fileNameSize + 1 + cpNameSize;
1793
Debug("CopyPasteRpcInGHGetNextFileCB: [%s] (%"FMTSZ"u)\n",
1794
CPName_Print(*result, *resultLen), *resultLen);
1801
*-----------------------------------------------------------------------------
1803
* CopyPaste_GetVmxCopyPasteVersion --
1805
* Ask the vmx for it's copy/paste version.
1808
* The copy/paste version the vmx supports, 1 if the vmx doesn't know
1809
* what we're talking about.
1814
*-----------------------------------------------------------------------------
1818
CopyPaste_GetVmxCopyPasteVersion(void)
1823
Debug("%s: enter\n", __FUNCTION__);
1824
if (!RpcOut_sendOne(&reply, &replyLen, "vmx.capability.copypaste_version")) {
1825
Debug("CopyPaste_GetVmxCopyPasteVersion: could not get VMX copyPaste "
1826
"version capability: %s\n", reply ? reply : "NULL");
1827
gVmxCopyPasteVersion = 1;
1829
gVmxCopyPasteVersion = atoi(reply);
1833
Debug("CopyPaste_GetVmxCopyPasteVersion: got version %d\n", gVmxCopyPasteVersion);
1834
return gVmxCopyPasteVersion;
1839
*-----------------------------------------------------------------------------
1841
* CopyPaste_RegisterCapability --
1843
* Register the "copypaste" capability. Sometimes this needs to be done
1844
* separately from the rest of copy/paste registration, so we provide it
1854
*-----------------------------------------------------------------------------
1858
CopyPaste_RegisterCapability(void)
1860
Debug("%s: enter\n", __FUNCTION__);
1861
/* Tell the VMX about the copyPaste version we support. */
1862
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.copypaste_version 2")) {
1863
Debug("CopyPaste_RegisterCapability: could not set guest copypaste "
1864
"version capability\n");
1865
gVmxCopyPasteVersion = 1;
1868
Debug("CopyPaste_RegisterCapability: set copypaste version 2\n");
1874
*-----------------------------------------------------------------------------
1876
* CopyPaste_Register --
1878
* Setup callbacks, initialize.
1886
*-----------------------------------------------------------------------------
1890
CopyPaste_Register(GtkWidget* mainWnd)
1892
Debug("%s: enter\n", __FUNCTION__);
1893
/* Text copy/paste initialization for all versions. */
1894
#ifndef GDK_SELECTION_CLIPBOARD
1895
GDK_SELECTION_CLIPBOARD = gdk_atom_intern("CLIPBOARD", FALSE);
1898
#ifndef GDK_SELECTION_TYPE_TIMESTAMP
1899
GDK_SELECTION_TYPE_TIMESTAMP = gdk_atom_intern("TIMESTAMP", FALSE);
1902
#ifndef GDK_SELECTION_TYPE_UTF8_STRING
1903
GDK_SELECTION_TYPE_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
1906
gFCPAtom[FCP_TARGET_INFO_GNOME_COPIED_FILES] =
1907
gdk_atom_intern(FCP_TARGET_NAME_GNOME_COPIED_FILES, FALSE);
1908
gFCPAtom[FCP_TARGET_INFO_URI_LIST] =
1909
gdk_atom_intern(FCP_TARGET_NAME_URI_LIST, FALSE);
1912
* String is always in supported list. FCP atoms will dynamically be
1913
* added and removed.
1915
gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY,
1916
GDK_SELECTION_TYPE_STRING, 0);
1917
gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD,
1918
GDK_SELECTION_TYPE_STRING, 0);
1919
gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY,
1920
GDK_SELECTION_TYPE_UTF8_STRING, 0);
1921
gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD,
1922
GDK_SELECTION_TYPE_UTF8_STRING, 0);
1924
gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_received",
1925
GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB), mainWnd);
1926
gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_get",
1927
GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB), mainWnd);
1928
gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_clear_event",
1929
GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB), mainWnd);
1931
RpcIn_RegisterCallback(gRpcIn, "copypaste.hg.data.set",
1932
CopyPasteRpcInHGSetDataCB, NULL);
1933
RpcIn_RegisterCallback(gRpcIn, "copypaste.hg.data.finish",
1934
CopyPasteRpcInHGDataFinishCB, NULL);
1935
RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.data.get",
1936
CopyPasteRpcInGHSetDataCB, NULL);
1937
RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.get.next.file",
1938
CopyPasteRpcInGHGetNextFileCB, NULL);
1939
RpcIn_RegisterCallback(gRpcIn, "copypaste.gh.finish",
1940
CopyPasteRpcInGHFinishCB, NULL);
1942
CopyPasteStateInit();
1945
return CopyPaste_RegisterCapability();
1950
*-----------------------------------------------------------------------------
1952
* CopyPaste_Unregister --
1954
* Cleanup copy/paste related things.
1960
* copy/paste is stopped, the rpc channel to the vmx is closed.
1962
*-----------------------------------------------------------------------------
1966
CopyPaste_Unregister(GtkWidget* mainWnd)
1968
Debug("%s: enter\n", __FUNCTION__);
1969
gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
1970
GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB),
1972
gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
1973
GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB),
1975
gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
1976
GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB),
1978
RpcIn_UnregisterCallback(gRpcIn, "copypaste.hg.data.set");
1979
RpcIn_UnregisterCallback(gRpcIn, "copypaste.hg.data.finish");
1980
RpcIn_UnregisterCallback(gRpcIn, "copypaste.gh.data.get");
1981
RpcIn_UnregisterCallback(gRpcIn, "copypaste.gh.get.next.file");
1982
RpcIn_UnregisterCallback(gRpcIn, "copypaste.gh.finish");
1987
*-----------------------------------------------------------------------------
1989
* CopyPaste_OnReset --
1991
* Handles reinitializing Copy Paste state on a reset.
1999
*-----------------------------------------------------------------------------
2003
CopyPaste_OnReset(void)
2005
Debug("%s: enter\n", __FUNCTION__);
2006
if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) {
2007
File_DeleteDirectoryTree(gFileRoot);
2008
if (DnD_BlockIsReady(&gBlockCtrl) &&
2009
!gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) {
2010
Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n",
2013
gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot);
2016
CopyPasteStateInit();
2020
*----------------------------------------------------------------------------
2022
* CopyPaste_InProgress --
2024
* Indicates whether a copy/paste data transfer is currently in progress.
2027
* TRUE if in progress, FALSE otherwise.
2032
*----------------------------------------------------------------------------
2036
CopyPaste_InProgress(void)
2038
/* XXX We currently have no way to determine if a G->H FCP is ongoing. */
2039
return gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING;
2044
*----------------------------------------------------------------------------
2046
* CopyPaste_IsRpcCPSupported --
2048
* Check if RPC copy/paste is supported by vmx or not.
2051
* TRUE if RPC copy/paste is supported, FALSE otherwise.
2056
*-----------------------------------------------------------------------------
2060
CopyPaste_IsRpcCPSupported(void)
2062
return gVmxCopyPasteVersion > 1;
2067
*----------------------------------------------------------------------------
2069
* CopyPasteStateInit --
2071
* Initalialize CopyPaste State.
2079
*----------------------------------------------------------------------------
2083
CopyPasteStateInit(void)
2085
Debug("%s: enter\n", __FUNCTION__);
2086
gHostClipboardBuf[0] = '\0';
2087
gGuestSelPrimaryBuf[0] = '\0';
2088
gGuestSelClipboardBuf[0] = '\0';
2090
gGHFCPRpcResultBuffer = NULL;
2091
gHGFCPPending = FALSE;
2092
gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET;
2094
if (CopyPaste_GetVmxCopyPasteVersion() >= 2) {
2096
* Create staging directory for file copy/paste. This is for vmx with version 2
2099
gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot);
2100
Debug("%s: create file root [%s]\n", __FUNCTION__, gFileRoot);