~ubuntu-branches/ubuntu/quantal/open-vm-tools/quantal-201210021442

« back to all changes in this revision

Viewing changes to vmware-user/copyPaste.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-03-31 14:20:05 UTC
  • mfrom: (1.4.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331142005-3n9red91p7ogkweo
Tags: 2011.03.28-387002-0ubuntu1
* Merge latest upstream git tag.  This has the unlocked_ioctl change
  needed to fix dkms build failures (LP: #727342)
* Changes in debian/rules:
  - work around a bug in toolbox/Makefile, where install-exec-hook is
    not happening.  This needs to get fixed the right way.
  - don't install 'vmware-user' which seems to no longer exist
  - move /etc/xdg into open-vm-toolbox (which should be done using .install)
* debian/open-vm-tools.init: add 'modprobe [-r] vmblock'. (LP: #332323)
* debian/rules and debian/open-vm-toolbox.lintian-overrides:
  - Make vmware-user-suid-wrapper suid-root (LP: #332323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*********************************************************
2
 
 * Copyright (C) 2005 VMware, Inc. All rights reserved.
3
 
 *
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.
7
 
 *
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.
12
 
 *
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.
16
 
 *
17
 
 *********************************************************/
18
 
 
19
 
/*
20
 
 * copyPaste.c --
21
 
 *
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.
26
 
 *
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.
31
 
 *
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
37
 
 *    selection text.
38
 
 *
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.
46
 
 *
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.
57
 
 */
58
 
 
59
 
#include "vmwareuserInt.h"
60
 
#include <stdlib.h>
61
 
#include <string.h>
62
 
#include <sys/time.h>
63
 
#include <unistd.h>
64
 
#include <errno.h>
65
 
 
66
 
#include "vm_assert.h"
67
 
#include "debug.h"
68
 
#include "str.h"
69
 
#include "strutil.h"
70
 
#include "eventManager.h"
71
 
#include "guestApp.h"
72
 
#include "dnd.h"
73
 
#include "util.h"
74
 
#include "cpName.h"
75
 
#include "cpNameUtil.h"
76
 
#include "guestInfoLib.h"
77
 
#include "vmblock.h"
78
 
#include "file.h"
79
 
#include "codeset.h"
80
 
#include "escape.h"
81
 
#include "hostinfo.h"
82
 
#include "wiper.h"
83
 
#include "vmware/guestrpc/tclodefs.h"
84
 
 
85
 
/*
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().
88
 
 */
89
 
#ifndef GDK_SELECTION_CLIPBOARD
90
 
GdkAtom GDK_SELECTION_CLIPBOARD;
91
 
#endif
92
 
 
93
 
#ifndef GDK_SELECTION_TYPE_TIMESTAMP
94
 
GdkAtom GDK_SELECTION_TYPE_TIMESTAMP;
95
 
#endif
96
 
 
97
 
#ifndef GDK_SELECTION_TYPE_UTF8_STRING
98
 
GdkAtom GDK_SELECTION_TYPE_UTF8_STRING;
99
 
#endif
100
 
 
101
 
typedef struct FCPGHState {
102
 
   char *fileList;
103
 
   char *fileListNext;
104
 
   size_t fileListSize;
105
 
} FCPGHState;
106
 
 
107
 
/*
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
112
 
 *
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
116
 
 */
117
 
static int32 gVmxCopyPasteVersion = 1;
118
 
 
119
 
/*
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.
122
 
 */
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];
129
 
 
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;
139
 
/* 
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.
142
 
 */
143
 
static uint64 gHGFCPTotalSize;
144
 
 
145
 
static GdkAtom gFCPAtom[NR_FCP_TARGETS];
146
 
 
147
 
/* Host->guest file transfer status, used for sync between transfer and paste. */
148
 
int gHGFCPFileTransferStatus;
149
 
 
150
 
static char gFileRoot[DND_MAX_PATH];
151
 
static size_t gFileRootSize;
152
 
static Bool gIsOwner;
153
 
static VmTimeType gHGGetListTime;
154
 
 
155
 
/*
156
 
 * Forward Declarations
157
 
 */
158
 
static INLINE void CopyPasteStateInit(void);
159
 
static void CopyPasteSelectionReceivedCB(GtkWidget *widget,
160
 
                                         GtkSelectionData *selection_data,
161
 
                                         gpointer data);
162
 
static void CopyPasteSelectionGetCB(GtkWidget *widget,
163
 
                                    GtkSelectionData *selection_data,
164
 
                                    guint info,
165
 
                                    guint time_stamp,
166
 
                                    gpointer data);
167
 
static gint CopyPasteSelectionClearCB(GtkWidget *widget,
168
 
                                      GdkEventSelection *event,
169
 
                                      gpointer data);
170
 
 
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);
178
 
 
179
 
static INLINE void CopyPasteGHFileListClear(void);
180
 
static INLINE void CopyPasteGHFileListSet(char *fileList, size_t fileListSize);
181
 
 
182
 
/* This struct is only used by CopyPasteSelectionRemoveTarget. */
183
 
struct SelectionTargetList {
184
 
  GdkAtom selection;
185
 
  GtkTargetList *list;
186
 
};
187
 
 
188
 
 
189
 
/*
190
 
 *-----------------------------------------------------------------------------
191
 
 *
192
 
 * CopyPasteSelectionRemoveTarget --
193
 
 *
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.
197
 
 *
198
 
 * Results:
199
 
 *      None.
200
 
 *
201
 
 * Side effects:
202
 
 *      If no more target, the selection list will be removed too.
203
 
 *
204
 
 *-----------------------------------------------------------------------------
205
 
 */
206
 
 
207
 
void
208
 
CopyPasteSelectionRemoveTarget(GtkWidget *widget,
209
 
                               GdkAtom selection,
210
 
                               GdkAtom target)
211
 
{
212
 
   const char *selection_handler_key = "gtk-selection-handlers";
213
 
   struct SelectionTargetList *targetList;
214
 
   GList *tempList;
215
 
   GList *selectionLists;
216
 
 
217
 
   /* Get selection list. */
218
 
   selectionLists = gtk_object_get_data(GTK_OBJECT (widget), selection_handler_key);
219
 
   tempList = selectionLists;
220
 
   while (tempList) {
221
 
      /* Enumerate the list to find the selection. */
222
 
      targetList = tempList->data;
223
 
      if (targetList->selection == selection) {
224
 
         /* Remove target. */
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);
230
 
            g_free(targetList);
231
 
            /* Remove and free selection node. */
232
 
            selectionLists = g_list_remove_link(selectionLists, tempList);
233
 
            g_list_free_1(tempList);
234
 
         }
235
 
         break;
236
 
      }
237
 
      tempList = tempList->next;
238
 
   }
239
 
   /* Put new selection list back. */
240
 
   gtk_object_set_data (GTK_OBJECT (widget), selection_handler_key, selectionLists);
241
 
}
242
 
 
243
 
 
244
 
/*
245
 
 *-----------------------------------------------------------------------------
246
 
 *
247
 
 * CopyPaste_RequestSelection --
248
 
 *
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
251
 
 *      copy/paste.
252
 
 *
253
 
 * Results:
254
 
 *      None.
255
 
 *
256
 
 * Side effects:
257
 
 *      The owner of the clipboard will get a request from our application.
258
 
 *
259
 
 *-----------------------------------------------------------------------------
260
 
 */
261
 
 
262
 
void
263
 
CopyPaste_RequestSelection(void)
264
 
{
265
 
   if (gVmxCopyPasteVersion > 1) {
266
 
      return;
267
 
   }
268
 
 
269
 
   /*
270
 
    * Ask for both the PRIMARY and CLIPBOARD selections.
271
 
    */
272
 
   gGuestSelPrimaryBuf[0] = '\0';
273
 
   gGuestSelClipboardBuf[0] = '\0';
274
 
 
275
 
   /* Only send out request if we are not the owner. */
276
 
   if (!gIsOwner) {
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,
282
 
                            GDK_CURRENT_TIME);
283
 
      while (gWaitingOnGuestSelection) gtk_main_iteration();
284
 
 
285
 
      gWaitingOnGuestSelection = TRUE;
286
 
      gtk_selection_convert(gUserMainWidget,
287
 
                            GDK_SELECTION_CLIPBOARD,
288
 
                            GDK_SELECTION_TYPE_UTF8_STRING,
289
 
                            GDK_CURRENT_TIME);
290
 
      while (gWaitingOnGuestSelection) gtk_main_iteration();
291
 
 
292
 
      if (gGuestSelPrimaryBuf[0] == '\0' && gGuestSelClipboardBuf[0] == '\0') {
293
 
         /*
294
 
          * If we cannot get utf8 text, try to get localized text from primary
295
 
          * and clipboard.
296
 
          */
297
 
         gWaitingOnGuestSelection = TRUE;
298
 
         gtk_selection_convert(gUserMainWidget,
299
 
                               GDK_SELECTION_PRIMARY,
300
 
                               GDK_SELECTION_TYPE_STRING,
301
 
                               GDK_CURRENT_TIME);
302
 
         while (gWaitingOnGuestSelection) gtk_main_iteration();
303
 
 
304
 
         gWaitingOnGuestSelection = TRUE;
305
 
         gtk_selection_convert(gUserMainWidget,
306
 
                               GDK_SELECTION_CLIPBOARD,
307
 
                               GDK_SELECTION_TYPE_STRING,
308
 
                               GDK_CURRENT_TIME);
309
 
         while (gWaitingOnGuestSelection) gtk_main_iteration();
310
 
      }
311
 
   }
312
 
   /* Send text to host. */
313
 
   Debug("CopyPaste_RequestSelection: Prim is [%s], Clip is [%s]\n",
314
 
         gGuestSelPrimaryBuf, gGuestSelClipboardBuf);
315
 
   CopyPasteSetBackdoorSelections();
316
 
}
317
 
 
318
 
 
319
 
/*
320
 
 *-----------------------------------------------------------------------------
321
 
 *
322
 
 * CopyPasteSelectionReceivedCB --
323
 
 *
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
329
 
 *      clipboard.
330
 
 *
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.
334
 
 *
335
 
 *      For guest->host copy/paste (both text and file).
336
 
 *
337
 
 * Results:
338
 
 *      None.
339
 
 *
340
 
 * Side effects:
341
 
 *      None.
342
 
 *
343
 
 *-----------------------------------------------------------------------------
344
 
 */
345
 
 
346
 
void
347
 
CopyPasteSelectionReceivedCB(GtkWidget *widget,                // IN: unused
348
 
                             GtkSelectionData *selection_data, // IN: requested data
349
 
                             gpointer data)                    // IN: unused
350
 
{
351
 
   char *target;
352
 
   char *utf8Str = NULL;
353
 
   size_t len;
354
 
   size_t aligned_len;
355
 
 
356
 
   if ((widget == NULL) || (selection_data == NULL)) {
357
 
      Debug("CopyPasteSelectionReceivedCB: Error, widget or selection_data is invalid\n");
358
 
      goto exit;
359
 
   }
360
 
 
361
 
   if (selection_data->length < 0) {
362
 
      Debug("CopyPasteSelectionReceivedCB: Error, length less than 0\n");
363
 
      goto exit;
364
 
   }
365
 
 
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);
377
 
         } else {
378
 
            Debug("CopyPasteSelectionReceivedCB: Unknown pri time. Size %d\n",
379
 
                  selection_data->length);
380
 
         }
381
 
      }
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);
391
 
         } else {
392
 
            Debug("CopyPasteSelectionReceivedCB: Unknown clip time. Size %d\n",
393
 
                  selection_data->length);
394
 
         }
395
 
      }
396
 
      goto exit;
397
 
   }
398
 
 
399
 
   if (selection_data->selection == GDK_SELECTION_PRIMARY) {
400
 
      target = gGuestSelPrimaryBuf;
401
 
   } else if (selection_data->selection == GDK_SELECTION_CLIPBOARD) {
402
 
      target = gGuestSelClipboardBuf;
403
 
   } else {
404
 
      goto exit;
405
 
   }
406
 
 
407
 
   utf8Str = selection_data->data;
408
 
   len = strlen(selection_data->data);
409
 
 
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");
415
 
      } else {
416
 
         memcpy(target, selection_data->data, len + 1);
417
 
      }
418
 
      goto exit;
419
 
   }
420
 
 
421
 
   /*
422
 
    * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code
423
 
    * set. Convert to utf8 before send to vmx.
424
 
    */
425
 
   if (selection_data->target == GDK_SELECTION_TYPE_STRING &&
426
 
       !CodeSet_CurrentToUtf8(selection_data->data,
427
 
                              selection_data->length,
428
 
                              &utf8Str,
429
 
                              &len)) {
430
 
      Debug("CopyPasteSelectionReceivedCB: Couldn't convert to utf8 code set\n");
431
 
      gWaitingOnGuestSelection = FALSE;
432
 
      return;
433
 
   }
434
 
 
435
 
   /*
436
 
    * String in backdoor communication is 4 bytes by 4 bytes, so the len
437
 
    * should be aligned to 4;
438
 
    */
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);
444
 
      } else {
445
 
         memcpy(target, utf8Str, MAX_SELECTION_BUFFER_LENGTH - 1);
446
 
         target[MAX_SELECTION_BUFFER_LENGTH - 1] ='\0';
447
 
      }
448
 
   } else {
449
 
      memcpy(target, utf8Str, len + 1);
450
 
   }
451
 
 
452
 
exit:
453
 
   if (selection_data->target == GDK_SELECTION_TYPE_STRING) {
454
 
      free(utf8Str);
455
 
   }
456
 
   gWaitingOnGuestSelection = FALSE;
457
 
}
458
 
 
459
 
 
460
 
/*
461
 
 *-----------------------------------------------------------------------------
462
 
 *
463
 
 * CopyPasteSelectionGetCB --
464
 
 *
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).
473
 
 *
474
 
 * Results:
475
 
 *      None
476
 
 *
477
 
 * Side effects:
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.
482
 
 *
483
 
 *-----------------------------------------------------------------------------
484
 
 */
485
 
 
486
 
void
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
493
 
{
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;
499
 
   size_t preLen = 0;
500
 
   size_t postLen = 0;
501
 
   int len = 0;
502
 
   char *text = NULL;
503
 
   size_t textLen = 1;
504
 
   Bool blockAdded = FALSE;
505
 
   Bool gnomeFCP = FALSE;
506
 
   VmTimeType curTime;
507
 
 
508
 
   if ((widget == NULL) || (selection_data == NULL)) {
509
 
      Debug("CopyPasteSelectionGetCB: Error, widget or selection_data is invalid\n");
510
 
      return;
511
 
   }
512
 
 
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);
518
 
 
519
 
      /*
520
 
       * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code
521
 
       * set. Convert from utf8 to local one.
522
 
       */
523
 
      if (GDK_SELECTION_TYPE_STRING == selection_data->target &&
524
 
          !CodeSet_Utf8ToCurrent(gHostClipboardBuf,
525
 
                                 strlen(gHostClipboardBuf),
526
 
                                 &outBuf,
527
 
                                 &len)) {
528
 
         Debug("CopyPasteSelectionGetCB: can not convert to current codeset\n");
529
 
         return;
530
 
      }
531
 
 
532
 
      gtk_selection_data_set(selection_data, selection_data->target, 8,
533
 
                             outBuf, len);
534
 
      Debug("CopyPasteSelectionGetCB: Set text [%s]\n", outBuf);
535
 
 
536
 
      if (GDK_SELECTION_TYPE_STRING == selection_data->target) {
537
 
         free(outBuf);
538
 
      }
539
 
 
540
 
      return;
541
 
   }
542
 
   
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");
546
 
      return;
547
 
   }
548
 
 
549
 
   if (!gHGIsClipboardFCP) {
550
 
      Debug("CopyPasteSelectionGetCB: no file list available\n");
551
 
      return;
552
 
   }
553
 
 
554
 
   /*
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.
558
 
    */
559
 
   Hostinfo_GetTimeOfDay(&curTime);
560
 
   if (curTime - gHGGetListTime < FCP_COPY_DELAY) {
561
 
      Debug("%s: waiting for delay\n", __FUNCTION__);
562
 
      return;
563
 
   }
564
 
 
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");
568
 
         return;
569
 
      }
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 "
574
 
               "with CPName");
575
 
         return;
576
 
      }
577
 
      gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRING;
578
 
   }
579
 
 
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);
584
 
         blockAdded = TRUE;
585
 
      } else {
586
 
         Warning("CopyPasteSelectionGetCB: Unable to add block [%s].\n", gFileRoot);
587
 
      }
588
 
   }
589
 
 
590
 
   if (!blockAdded) {
591
 
      /*
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.
598
 
       */
599
 
      Debug("CopyPasteSelectionGetCB no blocking driver, waiting for "
600
 
            "HG file copy done ...\n");
601
 
      while (gHGFCPFileTransferStatus != FCP_FILE_TRANSFERRED) {
602
 
         struct timeval tv;
603
 
         int nr;
604
 
 
605
 
         tv.tv_sec = 0;
606
 
         nr = EventManager_ProcessNext(gEventQueue, (uint64 *)&tv.tv_usec);
607
 
         if (nr != 1) {
608
 
            Debug("CopyPasteSelectionGetCB unexpected end of loop: returned "
609
 
                  "value is %d.\n", nr);
610
 
            return;
611
 
         }
612
 
         if (select(0, NULL, NULL, NULL, &tv) == -1) {
613
 
            Debug("CopyPasteSelectionGetCB error in select (%s).\n", 
614
 
                  strerror(errno));
615
 
            return;         
616
 
         }
617
 
      }
618
 
 
619
 
      Debug("CopyPasteSelectionGetCB file transfer done!\n");
620
 
   }
621
 
 
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;
629
 
   }
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;
636
 
      gnomeFCP = TRUE;
637
 
 
638
 
      textLen += 5;
639
 
      text = Util_SafeRealloc(text, textLen);
640
 
      Str_Snprintf(text, 6, "copy\n");
641
 
   }
642
 
 
643
 
   if (!pre) {
644
 
      Debug("CopyPasteSelectionGetCB: invalid drag target info\n");
645
 
      return;
646
 
   }
647
 
 
648
 
 
649
 
   /*
650
 
    * Set begin to first non-NUL character and end to last NUL character to
651
 
    * prevent errors in calling CPName_GetComponent().
652
 
    */
653
 
   for(begin = gHostClipboardBuf; *begin == '\0'; begin++)
654
 
      ;
655
 
   end = CPNameUtil_Strrchr(gHostClipboardBuf, gGHFCPListSize + 1, '\0');
656
 
   ASSERT(end);
657
 
 
658
 
   /* Build up selection data */
659
 
   while ((len = CPName_GetComponent(begin, end, &next)) != 0) {
660
 
      const size_t origTextLen = textLen;
661
 
      Bool freeBegin = FALSE;
662
 
 
663
 
      if (len < 0) {
664
 
         Debug("CopyPasteSelectionGetCB: error getting next component\n");
665
 
         if (text) {
666
 
            free(text);
667
 
         }
668
 
         return;
669
 
      }
670
 
 
671
 
      /*
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.
675
 
       */
676
 
      if (selection_data->target == gFCPAtom[FCP_TARGET_INFO_URI_LIST]) {
677
 
         size_t newLen;
678
 
         char *escapedComponent;
679
 
         int escIndex;
680
 
         int bytesToEsc[256] = { 0, };
681
 
 
682
 
         /* We escape the following characters based on RFC 1630. */
683
 
         bytesToEsc['#'] = 1;
684
 
         bytesToEsc['?'] = 1;
685
 
         bytesToEsc['*'] = 1;
686
 
         bytesToEsc['!'] = 1;
687
 
         bytesToEsc['%'] = 1;  /* Escape character */
688
 
 
689
 
         /* Escape non-ASCII characters so we can pass UTF-8 filenames */
690
 
         for (escIndex = 0x80; escIndex < 0x100; escIndex++) {
691
 
            bytesToEsc[escIndex] = 1;
692
 
         }
693
 
 
694
 
         escapedComponent = Escape_Do('%', bytesToEsc, begin, len, &newLen);
695
 
         if (escapedComponent) {
696
 
            begin = escapedComponent;
697
 
            len = newLen;
698
 
            freeBegin = TRUE;
699
 
         }
700
 
      }
701
 
 
702
 
      /*
703
 
       * Append component.  NUL terminator was accounted for by initializing
704
 
       * textLen to one above.
705
 
       */
706
 
      textLen += preLen + len + postLen;
707
 
      text = Util_SafeRealloc(text, textLen);
708
 
 
709
 
      /* 
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
712
 
       * this problem on:
713
 
       * - KDE which asks for URI lists during FCP
714
 
       * - DnD in both Gnome and KDE since they ask for URI lists.
715
 
       *
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.
719
 
       */
720
 
      Str_Snprintf(text + origTextLen - 1,
721
 
                   textLen - origTextLen + 1,
722
 
                   "%s%s%s", pre, begin, gnomeFCP && next == end ? "" : post);
723
 
 
724
 
      if (freeBegin) {
725
 
         free((void *)begin);
726
 
      }
727
 
 
728
 
      /* Iterate to next component */
729
 
      begin = next;
730
 
   }
731
 
 
732
 
   /*
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.
736
 
    */
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. */
740
 
                          text, textLen);
741
 
   free(text);
742
 
}
743
 
 
744
 
 
745
 
/*
746
 
 *-----------------------------------------------------------------------------
747
 
 *
748
 
 * CopyPasteSelectionClearCB --
749
 
 *
750
 
 *      Callback for the gtk signal "selection_clear".
751
 
 *
752
 
 * Results:
753
 
 *      Always TRUE.
754
 
 *
755
 
 * Side effects:
756
 
 *      None
757
 
 *
758
 
 *-----------------------------------------------------------------------------
759
 
 */
760
 
 
761
 
static gint
762
 
CopyPasteSelectionClearCB(GtkWidget          *widget,         // IN: unused
763
 
                          GdkEventSelection  *event,          // IN: unused
764
 
                          gpointer           data)            // IN: unused
765
 
{
766
 
   Debug("CopyPasteSelectionClearCB got clear signal\n");
767
 
   gIsOwner = FALSE;
768
 
   return TRUE;
769
 
}
770
 
 
771
 
 
772
 
/*
773
 
 *-----------------------------------------------------------------------------
774
 
 *
775
 
 * CopyPasteSetBackdoorSelections --
776
 
 *
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
784
 
 *      compatibility.
785
 
 *
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.
789
 
 *
790
 
 *      XXX: The "new way" doesn't exist yet, the vmx has no support for it.
791
 
 *
792
 
 * Results:
793
 
 *      None.
794
 
 *
795
 
 * Side effects:
796
 
 *      The VMX probably changes some string buffers.
797
 
 *
798
 
 *-----------------------------------------------------------------------------
799
 
 */
800
 
 
801
 
void
802
 
CopyPasteSetBackdoorSelections(void)
803
 
{
804
 
   uint32 const *p;
805
 
   size_t len;
806
 
   size_t aligned_len;
807
 
   size_t primaryLen;
808
 
   size_t clipboardLen;
809
 
   unsigned int i;
810
 
 
811
 
   primaryLen = strlen(gGuestSelPrimaryBuf);
812
 
   clipboardLen = strlen(gGuestSelClipboardBuf);
813
 
 
814
 
   if (primaryLen) {
815
 
      /*
816
 
       * Send primary selection to backdoor if it exists.
817
 
       */
818
 
      p = (uint32 const *)gGuestSelPrimaryBuf;
819
 
   } else if (clipboardLen) {
820
 
      /*
821
 
       * Otherwise send clipboard to backdoor if it exists.
822
 
       */
823
 
      p = (uint32 const *)gGuestSelClipboardBuf;
824
 
   } else {
825
 
      /*
826
 
       * Neither selection is set
827
 
       */
828
 
      p = NULL;
829
 
   }
830
 
 
831
 
   if (p == NULL) {
832
 
      GuestApp_SetSelLength(0);
833
 
      Debug("CopyPasteSetBackdoorSelections Set empty text.\n");
834
 
   } else {
835
 
      len = strlen((char *)p);
836
 
      Debug("CopyPasteSetBackdoorSelections Set text [%s].\n", (char *)p);
837
 
      aligned_len = (len + 4) & ~3;
838
 
 
839
 
      /* Here long string should already be truncated. */
840
 
      ASSERT(aligned_len <= MAX_SELECTION_BUFFER_LENGTH);
841
 
 
842
 
      GuestApp_SetSelLength(len);
843
 
      for (i = 0; i < len; i += 4, p++) {
844
 
         GuestApp_SetNextPiece(*p);
845
 
      }
846
 
   }
847
 
}
848
 
 
849
 
 
850
 
/*
851
 
 *-----------------------------------------------------------------------------
852
 
 *
853
 
 * CopyPaste_GetBackdoorSelections --
854
 
 *
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
859
 
 *      on the host.
860
 
 *
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
864
 
 *
865
 
 * Results:
866
 
 *      TRUE if selection length>=0, FLASE otherwise.
867
 
 *
868
 
 * Side effects:
869
 
 *      This application becomes the selection owner for PRIMARY and/or
870
 
        CLIPBOARD selections.
871
 
 *
872
 
 *-----------------------------------------------------------------------------
873
 
 */
874
 
 
875
 
Bool
876
 
CopyPaste_GetBackdoorSelections(void)
877
 
{
878
 
   int selLength;
879
 
   int iAtom;
880
 
 
881
 
   if (gVmxCopyPasteVersion > 1) {
882
 
      return TRUE;
883
 
   }
884
 
 
885
 
   selLength = GuestApp_GetHostSelectionLen();
886
 
   if (selLength < 0) {
887
 
      return FALSE;
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,
894
 
                              GDK_CURRENT_TIME);
895
 
      gtk_selection_owner_set(gUserMainWidget,
896
 
                              GDK_SELECTION_PRIMARY,
897
 
                              GDK_CURRENT_TIME);
898
 
      gIsOwner = TRUE;
899
 
      gHGIsClipboardFCP = FALSE;
900
 
      for (iAtom = 0; iAtom < NR_FCP_TARGETS; iAtom++) {
901
 
         CopyPasteSelectionRemoveTarget(gUserMainWidget,
902
 
                                        GDK_SELECTION_PRIMARY,
903
 
                                        gFCPAtom[iAtom]);
904
 
         CopyPasteSelectionRemoveTarget(gUserMainWidget,
905
 
                                        GDK_SELECTION_CLIPBOARD,
906
 
                                        gFCPAtom[iAtom]);
907
 
      }
908
 
   }
909
 
   return TRUE;
910
 
}
911
 
 
912
 
 
913
 
/*
914
 
 *-----------------------------------------------------------------------------
915
 
 *
916
 
 * CopyPasteRpcInGHSetDataCB --
917
 
 *
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.
922
 
 *
923
 
 *    For Guest->Host copy/paste operations only.
924
 
 *
925
 
 * Results:
926
 
 *    TRUE on success, FALSE on failure.
927
 
 *
928
 
 * Side effects:
929
 
 *    The owner of the clipboard will get requests from our application.
930
 
 *
931
 
 *-----------------------------------------------------------------------------
932
 
 */
933
 
 
934
 
static Bool
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
941
 
{
942
 
   int iAtom;
943
 
   GdkAtom activeSelection = GDK_SELECTION_PRIMARY;
944
 
   char *source = gGuestSelPrimaryBuf;
945
 
   char format[256];
946
 
   char *rpcBody = NULL;
947
 
   size_t rpcBodySize = 0;
948
 
 
949
 
   gGuestSelPrimaryBuf[0] = '\0';
950
 
   gGuestSelClipboardBuf[0] = '\0';
951
 
 
952
 
   if (gIsOwner) {
953
 
      Debug("CopyPasteRpcInGHSetDataCB Send empty buf to host\n");
954
 
      return RpcIn_SetRetVals(result, resultLen, "", TRUE);
955
 
   }
956
 
 
957
 
   /* First check which one is newer, primary selection or clipboard. */
958
 
   gGuestSelPrimaryTime = 0;
959
 
   gGuestSelClipboardTime = 0;
960
 
 
961
 
   gWaitingOnGuestSelection = TRUE;
962
 
   gtk_selection_convert(gUserMainWidget,
963
 
                         GDK_SELECTION_PRIMARY,
964
 
                         GDK_SELECTION_TYPE_TIMESTAMP,
965
 
                         GDK_CURRENT_TIME);
966
 
   while (gWaitingOnGuestSelection) gtk_main_iteration();
967
 
 
968
 
   gWaitingOnGuestSelection = TRUE;
969
 
   gtk_selection_convert(gUserMainWidget,
970
 
                         GDK_SELECTION_CLIPBOARD,
971
 
                         GDK_SELECTION_TYPE_TIMESTAMP,
972
 
                         GDK_CURRENT_TIME);
973
 
   while (gWaitingOnGuestSelection) gtk_main_iteration();
974
 
 
975
 
   if (gGuestSelPrimaryTime < gGuestSelClipboardTime) {
976
 
      activeSelection = GDK_SELECTION_CLIPBOARD;
977
 
      source = gGuestSelClipboardBuf;
978
 
   }
979
 
 
980
 
try_again:
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,
985
 
                            activeSelection,
986
 
                            gFCPAtom[iAtom],
987
 
                            GDK_CURRENT_TIME);
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);
995
 
         }
996
 
         break;
997
 
      }
998
 
   }
999
 
 
1000
 
   if (source[0] != '\0') {
1001
 
      char *currName;
1002
 
      size_t currSize;
1003
 
      size_t index = 0;
1004
 
      char *ghFileList = NULL;
1005
 
      size_t ghFileListSize = 0;
1006
 
 
1007
 
      /*
1008
 
       * In gnome, before file list there may be a extra line indicating it
1009
 
       * is a copy or cut.
1010
 
       */
1011
 
      if (strncmp(source, "copy", 4) == 0) {
1012
 
         source += 4;
1013
 
      }
1014
 
      if (strncmp(source, "cut", 3) == 0) {
1015
 
         source += 3;
1016
 
      }
1017
 
 
1018
 
      while (*source == '\n' || *source == '\r' || *source == ' ') {
1019
 
         source++;
1020
 
      }
1021
 
 
1022
 
      /*
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.
1027
 
       */
1028
 
      while ((currName = DnD_UriListGetNextFile(source,
1029
 
                                                &index,
1030
 
                                                &currSize))) {
1031
 
         size_t lastComponentSize;
1032
 
         char *lastComponentStart;
1033
 
 
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';
1040
 
         ghFileListSize++;
1041
 
 
1042
 
         /* Append last component to RPC body */
1043
 
         lastComponentStart = CPNameUtil_Strrchr(currName, currSize, DIRSEPC);
1044
 
         if (!lastComponentStart) {
1045
 
            /*
1046
 
             * This shouldn't happen since filenames are absolute, but handle
1047
 
             * it as if the file name is the last component
1048
 
             */
1049
 
            lastComponentStart = currName;
1050
 
         } else {
1051
 
            /* Skip the last directory separator */
1052
 
            lastComponentStart++;
1053
 
         }
1054
 
 
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';
1060
 
         rpcBodySize++;
1061
 
 
1062
 
         free(currName);
1063
 
      }
1064
 
 
1065
 
      if (!ghFileList || !rpcBody) {
1066
 
         Warning("CopyPasteRpcInGHSetDataCB: no filenames retrieved "
1067
 
                 "from URI list\n");
1068
 
         free(ghFileList);
1069
 
         free(rpcBody);
1070
 
         return RpcIn_SetRetVals(result, resultLen,
1071
 
                                 "error retrieving file name", FALSE);
1072
 
      }
1073
 
 
1074
 
      /* Set the list of full paths */
1075
 
      CopyPasteGHFileListSet(ghFileList, ghFileListSize);
1076
 
 
1077
 
      /* rpcBody (and its size) will always contain a trailing NUL character */
1078
 
      rpcBodySize--;
1079
 
      Debug("CopyPasteRpcInGHSetDataCB: Sending: [%s] (%zu)\n",
1080
 
            CPName_Print(rpcBody, rpcBodySize), rpcBodySize);
1081
 
 
1082
 
      Str_Sprintf(format, sizeof format, "%d ", CPFORMAT_FILELIST);
1083
 
      *resultLen = rpcBodySize + strlen(format);
1084
 
 
1085
 
      free(gGHFCPRpcResultBuffer);
1086
 
      gGHFCPRpcResultBuffer = Util_SafeCalloc(1, rpcBodySize + strlen(format));
1087
 
 
1088
 
      memcpy(gGHFCPRpcResultBuffer, format, strlen(format));
1089
 
      memcpy(gGHFCPRpcResultBuffer + strlen(format), rpcBody, rpcBodySize);
1090
 
      free(rpcBody);
1091
 
      *result = gGHFCPRpcResultBuffer;
1092
 
      return TRUE;
1093
 
   } else {
1094
 
      /* Try to get utf8 text from active selection. */
1095
 
      gWaitingOnGuestSelection = TRUE;
1096
 
      gtk_selection_convert(gUserMainWidget,
1097
 
                            activeSelection,
1098
 
                            GDK_SELECTION_TYPE_UTF8_STRING,
1099
 
                            GDK_CURRENT_TIME);
1100
 
      while (gWaitingOnGuestSelection) gtk_main_iteration();
1101
 
 
1102
 
      if (source[0] == '\0') {
1103
 
         /* Try to get text from active selection. */
1104
 
         gWaitingOnGuestSelection = TRUE;
1105
 
         gtk_selection_convert(gUserMainWidget,
1106
 
                              activeSelection,
1107
 
                              GDK_SELECTION_TYPE_STRING,
1108
 
                              GDK_CURRENT_TIME);
1109
 
         while (gWaitingOnGuestSelection) gtk_main_iteration();
1110
 
      }
1111
 
 
1112
 
      /*
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.
1118
 
       */
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;
1126
 
         goto try_again;
1127
 
      }
1128
 
 
1129
 
      if (source[0] != '\0') {
1130
 
         free(gGHFCPRpcResultBuffer);
1131
 
 
1132
 
         gGHFCPRpcResultBuffer =
1133
 
            Str_Asprintf(NULL, "%d %s", CPFORMAT_TEXT, source);
1134
 
 
1135
 
         if (!gGHFCPRpcResultBuffer) {
1136
 
            Debug("CopyPasteRpcInGHSetDataCB failed to alloc memory.\n");
1137
 
            return RpcIn_SetRetVals(result, resultLen,
1138
 
                                    "error allocating memory", FALSE);
1139
 
         }
1140
 
 
1141
 
         *result = gGHFCPRpcResultBuffer;
1142
 
         *resultLen = strlen(gGHFCPRpcResultBuffer);
1143
 
 
1144
 
         Debug("CopyPasteRpcInGHSetDataCB creating text: %s\n", source);
1145
 
 
1146
 
         return TRUE;
1147
 
      }
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);
1151
 
   }
1152
 
}
1153
 
 
1154
 
 
1155
 
/*
1156
 
 *----------------------------------------------------------------------------
1157
 
 *
1158
 
 * CopyPasteRpcInGHFinishCB --
1159
 
 *
1160
 
 *    For Guest->Host operations only.
1161
 
 *
1162
 
 *    Invoked when host side of copyPaste operation has finished.
1163
 
 *
1164
 
 * Results:
1165
 
 *    TRUE on success, FALSE on failure.
1166
 
 *
1167
 
 * Side effects:
1168
 
 *    None.
1169
 
 *
1170
 
 *----------------------------------------------------------------------------
1171
 
 */
1172
 
 
1173
 
static Bool
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
1180
 
{
1181
 
   char *effect = NULL;
1182
 
   unsigned int index = 0;
1183
 
 
1184
 
   gFcpGHState.fileListNext = gFcpGHState.fileList;
1185
 
 
1186
 
   effect = StrUtil_GetNextToken(&index, args, " ");
1187
 
   if (!effect) {
1188
 
      Warning("CopyPasteRpcInGHFinishCB: no drop effect provided\n");
1189
 
      return RpcIn_SetRetVals(result, resultLen,
1190
 
                              "drop effect not provided", FALSE);
1191
 
   }
1192
 
 
1193
 
   Debug("CopyPasteRpcInGHFinishCB got effect %s\n", effect);
1194
 
 
1195
 
   free(effect);
1196
 
   return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1197
 
}
1198
 
 
1199
 
 
1200
 
/*
1201
 
 *----------------------------------------------------------------------------
1202
 
 *
1203
 
 * CopyPasteGHFileListClear --
1204
 
 *
1205
 
 *    Clears existing Guest->Host file list, releasing any used resources.
1206
 
 *
1207
 
 * Results:
1208
 
 *    None.
1209
 
 *
1210
 
 * Side effects:
1211
 
 *    None.
1212
 
 *
1213
 
 *----------------------------------------------------------------------------
1214
 
 */
1215
 
 
1216
 
static INLINE void
1217
 
CopyPasteGHFileListClear(void)
1218
 
{
1219
 
   Debug("CopyPasteGHFileListClear: clearing G->H file list\n");
1220
 
   if (gFcpGHState.fileList) {
1221
 
      free(gFcpGHState.fileList);
1222
 
      gFcpGHState.fileList = NULL;
1223
 
   }
1224
 
   gFcpGHState.fileListSize = 0;
1225
 
   gFcpGHState.fileListNext = NULL;
1226
 
}
1227
 
 
1228
 
 
1229
 
/*
1230
 
 *----------------------------------------------------------------------------
1231
 
 *
1232
 
 * CopyPasteGHFileListSet --
1233
 
 *
1234
 
 *    Sets the Guest->Host file list that is accessed through
1235
 
 *    CopyPasteGHFileListGetNext().
1236
 
 *
1237
 
 * Results:
1238
 
 *    None.
1239
 
 *
1240
 
 * Side effects:
1241
 
 *    Clears the existing Guest->Host file list if it exists.
1242
 
 *
1243
 
 *----------------------------------------------------------------------------
1244
 
 */
1245
 
 
1246
 
static INLINE void
1247
 
CopyPasteGHFileListSet(char *fileList,      // IN: new Guest->Host file list
1248
 
                       size_t fileListSize) // IN: size of the provided list
1249
 
{
1250
 
   CopyPasteGHFileListClear();
1251
 
   gFcpGHState.fileList = fileList;
1252
 
   gFcpGHState.fileListSize = fileListSize;
1253
 
   gFcpGHState.fileListNext = fileList;
1254
 
 
1255
 
   Debug("CopyPasteGHFileListSet: [%s] (%"FMTSZ"u)\n",
1256
 
         CPName_Print(gFcpGHState.fileList, gFcpGHState.fileListSize),
1257
 
         gFcpGHState.fileListSize);
1258
 
}
1259
 
 
1260
 
 
1261
 
/*
1262
 
 *----------------------------------------------------------------------------
1263
 
 *
1264
 
 * CopyPasteGHFileListGetNext --
1265
 
 *
1266
 
 *    Retrieves the next file in the Guest->Host file list.
1267
 
 *
1268
 
 *    Note that this function may only be called after calling
1269
 
 *    CopyPasteGHFileListSet() and before calling CopyPasteGHFileListClear().
1270
 
 *
1271
 
 * Results:
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.
1275
 
 *
1276
 
 * Side effects:
1277
 
 *    The fileListNext value of the Guest->Host global state is updated.
1278
 
 *
1279
 
 *----------------------------------------------------------------------------
1280
 
 */
1281
 
 
1282
 
Bool
1283
 
CopyPasteGHFileListGetNext(char **fileName,       // OUT: fill with filename location
1284
 
                           size_t *fileNameSize)  // OUT: fill with filename length
1285
 
{
1286
 
   char const *end;
1287
 
   char const *next;
1288
 
   int len;
1289
 
 
1290
 
   ASSERT(gFcpGHState.fileList);
1291
 
   ASSERT(gFcpGHState.fileListNext);
1292
 
   ASSERT(gFcpGHState.fileListSize > 0);
1293
 
 
1294
 
   /* Ensure end is the last NUL character */
1295
 
   end = CPNameUtil_Strrchr(gFcpGHState.fileList,
1296
 
                            gFcpGHState.fileListSize,
1297
 
                            '\0');
1298
 
   ASSERT(end);
1299
 
 
1300
 
   /* Get the length of this filename and a pointer to the next one */
1301
 
   len = CPName_GetComponent(gFcpGHState.fileListNext, end, &next);
1302
 
   if (len < 0) {
1303
 
      Warning("CopyPasteGHFileListGetNext: error retrieving next component\n");
1304
 
      return FALSE;
1305
 
   }
1306
 
 
1307
 
   /* No more entries in the list */
1308
 
   if (len == 0) {
1309
 
      Debug("CopyPasteGHFileListGetNext: no more entries\n");
1310
 
      *fileName = NULL;
1311
 
      *fileNameSize = 0;
1312
 
      gFcpGHState.fileListNext = gFcpGHState.fileList;
1313
 
      return TRUE;
1314
 
   }
1315
 
 
1316
 
   Debug("CopyPasteGHFileListGetNext: returning [%s] (%d)\n",
1317
 
         gFcpGHState.fileListNext, len);
1318
 
 
1319
 
   *fileName = gFcpGHState.fileListNext;
1320
 
   *fileNameSize = len;
1321
 
   gFcpGHState.fileListNext = (char *)next;
1322
 
   return TRUE;
1323
 
}
1324
 
 
1325
 
 
1326
 
/*
1327
 
 *-----------------------------------------------------------------------------
1328
 
 *
1329
 
 * CopyPasteHGSetData --
1330
 
 *
1331
 
 *    Host is sending text for copy/paste.
1332
 
 *
1333
 
 *    RPC command format:
1334
 
 *    1. Format
1335
 
 *    2. Size of text
1336
 
 *    3. If text size > 0, then followed by text, otherwise nothing
1337
 
 *
1338
 
 *    For Host->Guest operations only.
1339
 
 *
1340
 
 * Results:
1341
 
 *    TRUE on success, FALSE otherwise.
1342
 
 *
1343
 
 * Side effects:
1344
 
 *    None.
1345
 
 *
1346
 
 *-----------------------------------------------------------------------------
1347
 
 */
1348
 
 
1349
 
static Bool
1350
 
CopyPasteHGSetData(char const **result,     // OUT
1351
 
                   size_t *resultLen,       // OUT
1352
 
                   const char *args)        // IN
1353
 
{
1354
 
   char *format = NULL;
1355
 
   char *sSize = NULL;
1356
 
   uint32 textSize;
1357
 
   unsigned int index = 0;
1358
 
   Bool ret = FALSE;
1359
 
   int iAtom;
1360
 
 
1361
 
   /* Parse value string. */
1362
 
   format = StrUtil_GetNextToken(&index, args, " ");
1363
 
   index++; /* Ignore leading space before data. */
1364
 
   sSize = StrUtil_GetNextToken(&index, args, " ");
1365
 
   index++;
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);
1370
 
      goto exit;
1371
 
   }
1372
 
 
1373
 
   textSize = atoi(sSize);
1374
 
   gHostClipboardBuf[0] = '\0';
1375
 
 
1376
 
   if (textSize > 0) {
1377
 
      if (textSize >= MAX_SELECTION_BUFFER_LENGTH) {
1378
 
         textSize = MAX_SELECTION_BUFFER_LENGTH - 1;
1379
 
      }
1380
 
      memcpy(gHostClipboardBuf, args + index, textSize);
1381
 
      gHostClipboardBuf[textSize] = '\0';
1382
 
      Debug("CopyPasteHGSetData: Set text [%s]\n", gHostClipboardBuf);
1383
 
   }
1384
 
      
1385
 
   gtk_selection_owner_set(gUserMainWidget,
1386
 
                           GDK_SELECTION_CLIPBOARD,
1387
 
                           GDK_CURRENT_TIME);
1388
 
   gtk_selection_owner_set(gUserMainWidget,
1389
 
                           GDK_SELECTION_PRIMARY,
1390
 
                           GDK_CURRENT_TIME);
1391
 
   gIsOwner = TRUE;
1392
 
   gHGIsClipboardFCP = FALSE;
1393
 
 
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,
1398
 
                                     gFCPAtom[iAtom]);
1399
 
      CopyPasteSelectionRemoveTarget(gUserMainWidget,
1400
 
                                     GDK_SELECTION_CLIPBOARD,
1401
 
                                     gFCPAtom[iAtom]);
1402
 
   }
1403
 
 
1404
 
   ret = RpcIn_SetRetVals(result, resultLen, "", TRUE);
1405
 
 
1406
 
exit:
1407
 
   free(format);
1408
 
   free(sSize);
1409
 
   return ret;
1410
 
}
1411
 
 
1412
 
 
1413
 
/*
1414
 
 *-----------------------------------------------------------------------------
1415
 
 *
1416
 
 * CopyPasteRpcInHGDataFinishCB --
1417
 
 *
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.
1421
 
 *
1422
 
 * Results:
1423
 
 *       TRUE on success, FALSE otherwise
1424
 
 *
1425
 
 * Side effects:
1426
 
 *       Copied files will be deleted in error or cancel case.
1427
 
 *
1428
 
 *-----------------------------------------------------------------------------
1429
 
 */
1430
 
 
1431
 
static Bool
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
1438
 
{
1439
 
   unsigned int index = 0;
1440
 
   char *state;
1441
 
 
1442
 
   Debug("CopyPasteRpcInHGDataFinishCB received copypaste data finish\n");
1443
 
 
1444
 
   state = StrUtil_GetNextToken(&index, args, " ");
1445
 
 
1446
 
   if (!state) {
1447
 
      Debug("CopyPasteRpcInHGDataFinishCB failed to parse data state\n");
1448
 
      return RpcIn_SetRetVals(result, resultLen,
1449
 
                              "must specify data finish state", FALSE);
1450
 
   }
1451
 
 
1452
 
   if (strcmp(state, "success") != 0) {
1453
 
      Debug("CopyPasteRpcInHGDataFinishCB data transfer error\n");
1454
 
      /*
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
1458
 
       * directory.
1459
 
       */
1460
 
      File_DeleteDirectoryTree(gFileRoot);
1461
 
   }
1462
 
 
1463
 
   free(state);
1464
 
 
1465
 
   ASSERT(gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING);
1466
 
   gHGFCPFileTransferStatus = FCP_FILE_TRANSFERRED;
1467
 
 
1468
 
   if (DnD_BlockIsReady(&gBlockCtrl) &&
1469
 
       !gBlockCtrl.RemoveBlock(gBlockCtrl.fd, gFileRoot)) {
1470
 
      Warning("CopyPasteRpcInHGDataFinishCB: Unable to remove block [%s].\n",
1471
 
              gFileRoot);
1472
 
   }
1473
 
 
1474
 
   /* get new root dir for next FCP operation. */
1475
 
   gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot);
1476
 
 
1477
 
   Debug("CopyPasteRpcInHGDataFinishCB create staging dir [%s]\n", gFileRoot);
1478
 
 
1479
 
   return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1480
 
}
1481
 
 
1482
 
 
1483
 
/*
1484
 
 *-----------------------------------------------------------------------------
1485
 
 *
1486
 
 * CopyPasteHGSetFileList --
1487
 
 *
1488
 
 *    Host is sending file list for FCP (file copy/paste).
1489
 
 *
1490
 
 *    RPC command format:
1491
 
 *    1. 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
1495
 
 *
1496
 
 *    For Host->Guest FCP operations only.
1497
 
 *
1498
 
 * Results:
1499
 
 *    TRUE on success, FALSE on failure.
1500
 
 *
1501
 
 * Side effects:
1502
 
 *    None.
1503
 
 *
1504
 
 *-----------------------------------------------------------------------------
1505
 
 */
1506
 
 
1507
 
static Bool
1508
 
CopyPasteHGSetFileList(char const **result,     // OUT
1509
 
                       size_t *resultLen,       // OUT
1510
 
                       const char *args)        // IN
1511
 
{
1512
 
   char *format = NULL;
1513
 
   char *data = NULL;
1514
 
   char *sListSize = NULL;
1515
 
   size_t listSize;
1516
 
   char *sTotalSize = NULL;
1517
 
   char *stagingDirName = NULL;
1518
 
   char mountDirName[DND_MAX_PATH];
1519
 
   unsigned int index = 0;
1520
 
   Bool ret = FALSE;
1521
 
   char *retStr;
1522
 
   int iAtom;
1523
 
   Bool usingDnDBlock;
1524
 
 
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, " ");
1530
 
   index++;
1531
 
   sListSize = StrUtil_GetNextToken(&index, args, " ");
1532
 
   index++;
1533
 
   if (!format || !sTotalSize || !sListSize) {
1534
 
      Debug("CopyPasteHGSetFileList failed to parse format & size\n");
1535
 
      retStr = "format or size is not completed";
1536
 
      goto exit;
1537
 
   }
1538
 
 
1539
 
   listSize = atoi(sListSize);
1540
 
   /* 
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.
1543
 
    */
1544
 
   gHGFCPTotalSize = atol(sTotalSize);
1545
 
 
1546
 
   if (listSize <= 0) {
1547
 
      Debug("CopyPasteHGSetFileList: got empty list\n");
1548
 
      gHostClipboardBuf[0] = '\0';
1549
 
      retStr = "";
1550
 
      ret = TRUE;
1551
 
      goto exit;
1552
 
   }
1553
 
 
1554
 
   /*
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.
1559
 
    */
1560
 
 
1561
 
   data = (char *)Util_SafeCalloc(1, listSize + 1);
1562
 
   memcpy(data, args + index, listSize);
1563
 
   data[listSize] = '\0';
1564
 
 
1565
 
   usingDnDBlock = DnD_BlockIsReady(&gBlockCtrl);
1566
 
   if (usingDnDBlock) {
1567
 
      /*
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.
1573
 
       */
1574
 
      stagingDirName = DnD_GetLastDirName(gFileRoot);
1575
 
      if (!stagingDirName) {
1576
 
         Debug("CopyPasteHGSetFileList: error construct stagingDirName\n");
1577
 
         retStr = "error construct stagingDirName";
1578
 
         goto exit;
1579
 
      }
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";
1584
 
         goto exit;
1585
 
      }
1586
 
      Str_Sprintf(mountDirName, sizeof mountDirName,
1587
 
                  "%s" DIRSEPS "%s" DIRSEPS, gBlockCtrl.blockRoot, stagingDirName);
1588
 
   }
1589
 
 
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";
1595
 
      goto exit;
1596
 
   }
1597
 
 
1598
 
   if (listSize + 1 > sizeof gHostClipboardBuf) {
1599
 
      Debug("CopyPasteHGSetFileList: data too large\n");
1600
 
      retStr = "data too large";
1601
 
      goto exit;
1602
 
   }
1603
 
 
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);
1609
 
 
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);
1615
 
   }
1616
 
 
1617
 
   Debug("CopyPasteHGSetFileList: added targets\n");
1618
 
   gtk_selection_owner_set(gUserMainWidget,
1619
 
                           GDK_SELECTION_CLIPBOARD,
1620
 
                           GDK_CURRENT_TIME);
1621
 
   gtk_selection_owner_set(gUserMainWidget,
1622
 
                           GDK_SELECTION_PRIMARY,
1623
 
                           GDK_CURRENT_TIME);
1624
 
   gIsOwner = TRUE;
1625
 
 
1626
 
   retStr = "";
1627
 
   ret = TRUE;
1628
 
 
1629
 
exit:
1630
 
   free(format);
1631
 
   free(data);
1632
 
   free(sTotalSize);
1633
 
   free(sListSize);
1634
 
   free(stagingDirName);
1635
 
   Hostinfo_GetTimeOfDay(&gHGGetListTime);
1636
 
   return RpcIn_SetRetVals(result, resultLen, retStr, ret);
1637
 
}
1638
 
 
1639
 
 
1640
 
/*
1641
 
 *-----------------------------------------------------------------------------
1642
 
 *
1643
 
 * CopyPasteRpcInHGSetDataCB --
1644
 
 *
1645
 
 *    Host is sending data for copy/paste. The data can be text, file list, etc.
1646
 
 *
1647
 
 *    For Host->Guest operations only.
1648
 
 *
1649
 
 * Results:
1650
 
 *    TRUE if success, FALSE otherwise.
1651
 
 *
1652
 
 * Side effects:
1653
 
 *    None.
1654
 
 *
1655
 
 *-----------------------------------------------------------------------------
1656
 
 */
1657
 
 
1658
 
static Bool
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
1665
 
{
1666
 
   char *formatStr = NULL;
1667
 
   DND_CPFORMAT format;
1668
 
   Bool ret = FALSE;
1669
 
 
1670
 
   unsigned int index = 0;
1671
 
 
1672
 
   if (gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING) {
1673
 
      return RpcIn_SetRetVals(result, resultLen,
1674
 
                              "", TRUE);
1675
 
   }
1676
 
 
1677
 
   /* Parse value string. */
1678
 
   formatStr = StrUtil_GetNextToken(&index, args, " ");
1679
 
   index++; /* Ignore leading space before data. */
1680
 
 
1681
 
   if (!formatStr) {
1682
 
      Debug("CopyPasteTcloHGDataSet failed to parse format\n");
1683
 
      return RpcIn_SetRetVals(result, resultLen,
1684
 
                              "format and size is not completed", FALSE);
1685
 
   }
1686
 
 
1687
 
   format = (DND_CPFORMAT)atoi(formatStr);
1688
 
   free(formatStr);
1689
 
 
1690
 
   switch (format) {
1691
 
   case CPFORMAT_TEXT:
1692
 
      ret = CopyPasteHGSetData(result, resultLen, args);
1693
 
      break;
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);
1700
 
      }
1701
 
      ret = CopyPasteHGSetFileList(result, resultLen, args);
1702
 
      break;
1703
 
   default:
1704
 
      Debug("CopyPasteTcloHGDataSet unknown format\n");
1705
 
      ret = RpcIn_SetRetVals(result, resultLen,
1706
 
                             "unknown format", FALSE);
1707
 
      break;
1708
 
   }
1709
 
 
1710
 
   return ret;
1711
 
}
1712
 
 
1713
 
 
1714
 
/*
1715
 
 *----------------------------------------------------------------------------
1716
 
 *
1717
 
 * CopyPasteRpcInGHGetNextFileCB --
1718
 
 *
1719
 
 *    For Guest->Host operations only.
1720
 
 *
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).
1725
 
 *
1726
 
 * Results:
1727
 
 *    TRUE on success, FALSE on failure.
1728
 
 *
1729
 
 * Side effects:
1730
 
 *    Iterator pointer within file list of GH state is iterated to next list
1731
 
 *    entry (through call to CopyPasteGHFileListGetNext()).
1732
 
 *
1733
 
 *----------------------------------------------------------------------------
1734
 
 */
1735
 
 
1736
 
static Bool
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
1743
 
{
1744
 
   static char resultBuffer[DND_MAX_PATH];
1745
 
   char *fileName;
1746
 
   size_t fileNameSize;
1747
 
   uint32 cpNameSize;
1748
 
   Bool res;
1749
 
 
1750
 
   /*
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.
1754
 
    */
1755
 
   res = CopyPasteGHFileListGetNext(&fileName, &fileNameSize);
1756
 
 
1757
 
   if (!res) {
1758
 
      Warning("CopyPasteRpcInGHGetNextFileCB: error retrieving file name\n");
1759
 
      return RpcIn_SetRetVals(result, resultLen, "error getting file", FALSE);
1760
 
   }
1761
 
 
1762
 
   if (!fileName) {
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);
1766
 
   }
1767
 
 
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);
1771
 
   }
1772
 
 
1773
 
   /*
1774
 
    * Construct a reply message of the form:
1775
 
    * <file name in guest format><NUL><filename in CPName format>
1776
 
    */
1777
 
   memcpy(resultBuffer, fileName, fileNameSize);
1778
 
   resultBuffer[fileNameSize] = '\0';
1779
 
 
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);
1787
 
   }
1788
 
 
1789
 
   /* Set manually because RpcIn_SetRetVals() assumes no NUL characters */
1790
 
   *result = resultBuffer;
1791
 
   *resultLen = fileNameSize + 1 + cpNameSize;
1792
 
 
1793
 
   Debug("CopyPasteRpcInGHGetNextFileCB: [%s] (%"FMTSZ"u)\n",
1794
 
         CPName_Print(*result, *resultLen), *resultLen);
1795
 
 
1796
 
   return TRUE;
1797
 
}
1798
 
 
1799
 
 
1800
 
/*
1801
 
 *-----------------------------------------------------------------------------
1802
 
 *
1803
 
 * CopyPaste_GetVmxCopyPasteVersion --
1804
 
 *
1805
 
 *      Ask the vmx for it's copy/paste version.
1806
 
 *
1807
 
 * Results:
1808
 
 *      The copy/paste version the vmx supports, 1 if the vmx doesn't know
1809
 
 *      what we're talking about.
1810
 
 *
1811
 
 * Side effects:
1812
 
 *      None
1813
 
 *
1814
 
 *-----------------------------------------------------------------------------
1815
 
 */
1816
 
 
1817
 
int32
1818
 
CopyPaste_GetVmxCopyPasteVersion(void)
1819
 
{
1820
 
   char *reply = NULL;
1821
 
   size_t replyLen;
1822
 
 
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;
1828
 
   } else {
1829
 
      gVmxCopyPasteVersion = atoi(reply);
1830
 
   }
1831
 
 
1832
 
   free(reply);
1833
 
   Debug("CopyPaste_GetVmxCopyPasteVersion: got version %d\n", gVmxCopyPasteVersion);
1834
 
   return gVmxCopyPasteVersion;
1835
 
}
1836
 
 
1837
 
 
1838
 
/*
1839
 
 *-----------------------------------------------------------------------------
1840
 
 *
1841
 
 * CopyPaste_RegisterCapability --
1842
 
 *
1843
 
 *      Register the "copypaste" capability. Sometimes this needs to be done
1844
 
 *      separately from the rest of copy/paste registration, so we provide it
1845
 
 *      separately here.
1846
 
 *
1847
 
 * Results:
1848
 
 *      TRUE on success
1849
 
 *      FALSE on failure
1850
 
 *
1851
 
 * Side effects:
1852
 
 *      None
1853
 
 *
1854
 
 *-----------------------------------------------------------------------------
1855
 
 */
1856
 
 
1857
 
Bool
1858
 
CopyPaste_RegisterCapability(void)
1859
 
{
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;
1866
 
      return FALSE;
1867
 
   }
1868
 
   Debug("CopyPaste_RegisterCapability: set copypaste version 2\n");
1869
 
   return TRUE;
1870
 
}
1871
 
 
1872
 
 
1873
 
/*
1874
 
 *-----------------------------------------------------------------------------
1875
 
 *
1876
 
 * CopyPaste_Register --
1877
 
 *
1878
 
 *      Setup callbacks, initialize.
1879
 
 *
1880
 
 * Results:
1881
 
 *      Always TRUE.
1882
 
 *
1883
 
 * Side effects:
1884
 
 *      None
1885
 
 *
1886
 
 *-----------------------------------------------------------------------------
1887
 
 */
1888
 
 
1889
 
Bool
1890
 
CopyPaste_Register(GtkWidget* mainWnd)
1891
 
{
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);
1896
 
#endif
1897
 
 
1898
 
#ifndef GDK_SELECTION_TYPE_TIMESTAMP
1899
 
   GDK_SELECTION_TYPE_TIMESTAMP = gdk_atom_intern("TIMESTAMP", FALSE);
1900
 
#endif
1901
 
 
1902
 
#ifndef GDK_SELECTION_TYPE_UTF8_STRING
1903
 
   GDK_SELECTION_TYPE_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
1904
 
#endif
1905
 
 
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);
1910
 
 
1911
 
   /* 
1912
 
    * String is always in supported list. FCP atoms will dynamically be 
1913
 
    * added and removed.
1914
 
    */
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);
1923
 
 
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);
1930
 
 
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);
1941
 
 
1942
 
   CopyPasteStateInit();
1943
 
   Wiper_Init(NULL);
1944
 
 
1945
 
   return CopyPaste_RegisterCapability();
1946
 
}
1947
 
 
1948
 
 
1949
 
/*
1950
 
 *-----------------------------------------------------------------------------
1951
 
 *
1952
 
 * CopyPaste_Unregister --
1953
 
 *
1954
 
 *      Cleanup copy/paste related things.
1955
 
 *
1956
 
 * Results:
1957
 
 *      None.
1958
 
 *
1959
 
 * Side effects:
1960
 
 *      copy/paste is stopped, the rpc channel to the vmx is closed.
1961
 
 *
1962
 
 *-----------------------------------------------------------------------------
1963
 
 */
1964
 
 
1965
 
void
1966
 
CopyPaste_Unregister(GtkWidget* mainWnd)
1967
 
{
1968
 
   Debug("%s: enter\n", __FUNCTION__);
1969
 
   gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
1970
 
                                 GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB),
1971
 
                                 mainWnd);
1972
 
   gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
1973
 
                                 GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB),
1974
 
                                 mainWnd);
1975
 
   gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
1976
 
                                 GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB),
1977
 
                                 mainWnd);
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");
1983
 
}
1984
 
 
1985
 
 
1986
 
/*
1987
 
 *-----------------------------------------------------------------------------
1988
 
 *
1989
 
 * CopyPaste_OnReset --
1990
 
 *
1991
 
 *    Handles reinitializing Copy Paste state on a reset.
1992
 
 *
1993
 
 * Results:
1994
 
 *    None.
1995
 
 *
1996
 
 * Side effects:
1997
 
 *    None.
1998
 
 *
1999
 
 *-----------------------------------------------------------------------------
2000
 
 */
2001
 
 
2002
 
void
2003
 
CopyPaste_OnReset(void)
2004
 
{
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",
2011
 
                 gFileRoot);
2012
 
      }
2013
 
      gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot);
2014
 
   }
2015
 
 
2016
 
   CopyPasteStateInit();
2017
 
}
2018
 
 
2019
 
/*
2020
 
 *----------------------------------------------------------------------------
2021
 
 *
2022
 
 * CopyPaste_InProgress --
2023
 
 *
2024
 
 *    Indicates whether a copy/paste data transfer is currently in progress.
2025
 
 *
2026
 
 * Results:
2027
 
 *    TRUE if in progress, FALSE otherwise.
2028
 
 *
2029
 
 * Side effects:
2030
 
 *    None.
2031
 
 *
2032
 
 *----------------------------------------------------------------------------
2033
 
 */
2034
 
 
2035
 
Bool
2036
 
CopyPaste_InProgress(void)
2037
 
{
2038
 
   /* XXX We currently have no way to determine if a G->H FCP is ongoing. */
2039
 
   return gHGFCPFileTransferStatus == FCP_FILE_TRANSFERRING;
2040
 
}
2041
 
 
2042
 
 
2043
 
/*
2044
 
 *----------------------------------------------------------------------------
2045
 
 *
2046
 
 * CopyPaste_IsRpcCPSupported --
2047
 
 *
2048
 
 *    Check if RPC copy/paste is supported by vmx or not.
2049
 
 *
2050
 
 * Results:
2051
 
 *    TRUE if RPC copy/paste is supported, FALSE otherwise.
2052
 
 *
2053
 
 * Side effects:
2054
 
 *    None.
2055
 
 *
2056
 
 *-----------------------------------------------------------------------------
2057
 
 */
2058
 
 
2059
 
Bool
2060
 
CopyPaste_IsRpcCPSupported(void)
2061
 
{
2062
 
   return gVmxCopyPasteVersion > 1;
2063
 
}
2064
 
 
2065
 
 
2066
 
/*
2067
 
 *----------------------------------------------------------------------------
2068
 
 *
2069
 
 * CopyPasteStateInit --
2070
 
 *
2071
 
 *    Initalialize CopyPaste State.
2072
 
 *
2073
 
 * Results:
2074
 
 *    None.
2075
 
 *
2076
 
 * Side effects:
2077
 
 *    None.
2078
 
 *
2079
 
 *----------------------------------------------------------------------------
2080
 
 */
2081
 
 
2082
 
void
2083
 
CopyPasteStateInit(void)
2084
 
{
2085
 
   Debug("%s: enter\n", __FUNCTION__);
2086
 
   gHostClipboardBuf[0] = '\0';
2087
 
   gGuestSelPrimaryBuf[0] = '\0';
2088
 
   gGuestSelClipboardBuf[0] = '\0';
2089
 
   gIsOwner = FALSE;
2090
 
   gGHFCPRpcResultBuffer = NULL;
2091
 
   gHGFCPPending = FALSE;
2092
 
   gHGFCPFileTransferStatus = FCP_FILE_TRANSFER_NOT_YET;
2093
 
 
2094
 
   if (CopyPaste_GetVmxCopyPasteVersion() >= 2) {
2095
 
      /*
2096
 
       * Create staging directory for file copy/paste. This is for vmx with version 2
2097
 
       * or greater.
2098
 
       */
2099
 
      gFileRootSize = DnD_GetNewFileRoot(gFileRoot, sizeof gFileRoot);
2100
 
      Debug("%s: create file root [%s]\n", __FUNCTION__, gFileRoot);
2101
 
   }
2102
 
}