VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp@ 102467

Last change on this file since 102467 was 102467, checked in by vboxsync, 15 months ago

Shared Clipboard: Make sure to check the event's rc when waiting for ShClX11ReadDataFromX11Async() to complete [build fix]. ​bugref:10384

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.0 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-x11.cpp 102467 2023-12-05 09:47:03Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Linux host.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
33#include <iprt/assert.h>
34#include <iprt/critsect.h>
35#include <iprt/env.h>
36#include <iprt/mem.h>
37#include <iprt/semaphore.h>
38#include <iprt/string.h>
39#include <iprt/asm.h>
40
41#include <VBox/GuestHost/SharedClipboard.h>
42#include <VBox/GuestHost/SharedClipboard-x11.h>
43#include <VBox/HostServices/VBoxClipboardSvc.h>
44#include <iprt/errcore.h>
45
46#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
47# include <VBox/GuestHost/SharedClipboard-transfers.h>
48# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
49# include <VBox/GuestHost/SharedClipboard-transfers.h>
50# endif
51#endif
52
53#include "VBoxSharedClipboardSvc-internal.h"
54#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
55# include "VBoxSharedClipboardSvc-transfers.h"
56#endif
57
58/* Number of currently extablished connections. */
59static volatile uint32_t g_cShClConnections;
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65/**
66 * Global context information used by the host glue for the X11 clipboard backend.
67 */
68struct SHCLCONTEXT
69{
70 /** This mutex is grabbed during any critical operations on the clipboard
71 * which might clash with others. */
72 RTCRITSECT CritSect;
73 /** X11 context data. */
74 SHCLX11CTX X11;
75 /** Pointer to the VBox host client data structure. */
76 PSHCLCLIENT pClient;
77 /** We set this when we start shutting down as a hint not to post any new
78 * requests. */
79 bool fShuttingDown;
80};
81
82
83/*********************************************************************************************************************************
84* Prototypes *
85*********************************************************************************************************************************/
86static DECLCALLBACK(int) shClSvcX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser);
87static DECLCALLBACK(int) shClSvcX11RequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser);
88
89#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
90static DECLCALLBACK(void) shClSvcX11TransferOnCreatedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
91static DECLCALLBACK(void) shClSvcX11TransferOnInitCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
92static DECLCALLBACK(void) shClSvcX11TransferOnDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
93static DECLCALLBACK(void) shClSvcX11TransferOnUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx);
94
95static DECLCALLBACK(int) shClSvcX11TransferIfaceHGRootListRead(PSHCLTXPROVIDERCTX pCtx);
96#endif
97
98
99/*********************************************************************************************************************************
100* Backend implementation *
101*********************************************************************************************************************************/
102int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
103{
104 RT_NOREF(pBackend);
105
106 LogFlowFuncEnter();
107
108 /* Override the connection limit. */
109 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
110 pTable->acMaxClients[i] = RT_MIN(VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX, pTable->acMaxClients[i]);
111
112 RT_ZERO(pBackend->Callbacks);
113 /* Use internal callbacks by default. */
114 pBackend->Callbacks.pfnReportFormats = shClSvcX11ReportFormatsCallback;
115 pBackend->Callbacks.pfnOnRequestDataFromSource = shClSvcX11RequestDataFromSourceCallback;
116
117 return VINF_SUCCESS;
118}
119
120void ShClBackendDestroy(PSHCLBACKEND pBackend)
121{
122 RT_NOREF(pBackend);
123
124 LogFlowFuncEnter();
125}
126
127void ShClBackendSetCallbacks(PSHCLBACKEND pBackend, PSHCLCALLBACKS pCallbacks)
128{
129#define SET_FN_IF_NOT_NULL(a_Fn) \
130 if (pCallbacks->pfn##a_Fn) \
131 pBackend->Callbacks.pfn##a_Fn = pCallbacks->pfn##a_Fn;
132
133 SET_FN_IF_NOT_NULL(ReportFormats);
134 SET_FN_IF_NOT_NULL(OnRequestDataFromSource);
135
136#undef SET_FN_IF_NOT_NULL
137}
138
139/**
140 * @note On the host, we assume that some other application already owns
141 * the clipboard and leave ownership to X11.
142 */
143int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
144{
145 int rc;
146
147 /* Check if maximum allowed connections count has reached. */
148 if (ASMAtomicIncU32(&g_cShClConnections) > VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX)
149 {
150 ASMAtomicDecU32(&g_cShClConnections);
151 LogRel(("Shared Clipboard: maximum amount for client connections reached\n"));
152 return VERR_OUT_OF_RESOURCES;
153 }
154
155 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
156 if (pCtx)
157 {
158 rc = RTCritSectInit(&pCtx->CritSect);
159 if (RT_SUCCESS(rc))
160 {
161 rc = ShClX11Init(&pCtx->X11, &pBackend->Callbacks, pCtx, fHeadless);
162 if (RT_SUCCESS(rc))
163 {
164 pClient->State.pCtx = pCtx;
165 pCtx->pClient = pClient;
166
167#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
168 /*
169 * Set callbacks.
170 * Those will be registered within ShClSvcTransferInit() when a new transfer gets initialized.
171 *
172 * Used for starting / stopping the HTTP server.
173 */
174 RT_ZERO(pClient->Transfers.Callbacks);
175
176 pClient->Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
177 pClient->Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
178
179 pClient->Transfers.Callbacks.pfnOnCreated = shClSvcX11TransferOnCreatedCallback;
180 pClient->Transfers.Callbacks.pfnOnInitialized = shClSvcX11TransferOnInitCallback;
181 pClient->Transfers.Callbacks.pfnOnDestroy = shClSvcX11TransferOnDestroyCallback;
182 pClient->Transfers.Callbacks.pfnOnUnregistered = shClSvcX11TransferOnUnregisteredCallback;
183#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
184
185 rc = ShClX11ThreadStart(&pCtx->X11, true /* grab shared clipboard */);
186 if (RT_FAILURE(rc))
187 ShClX11Destroy(&pCtx->X11);
188 }
189
190 if (RT_FAILURE(rc))
191 RTCritSectDelete(&pCtx->CritSect);
192 }
193
194 if (RT_FAILURE(rc))
195 {
196 pClient->State.pCtx = NULL;
197 RTMemFree(pCtx);
198 }
199 }
200 else
201 rc = VERR_NO_MEMORY;
202
203 if (RT_FAILURE(rc))
204 {
205 /* Restore active connections count. */
206 ASMAtomicDecU32(&g_cShClConnections);
207 }
208
209 LogFlowFuncLeaveRC(rc);
210 return rc;
211}
212
213int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
214{
215 RT_NOREF(pBackend);
216
217 LogFlowFuncEnter();
218
219 /* Tell the guest we have no data in case X11 is not available. If
220 * there is data in the host clipboard it will automatically be sent to
221 * the guest when the clipboard starts up. */
222 return ShClSvcReportFormats(pClient, VBOX_SHCL_FMT_NONE);
223}
224
225/**
226 * Shuts down the shared clipboard service and "disconnect" the guest.
227 * Note! Host glue code
228 */
229int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
230{
231 RT_NOREF(pBackend);
232
233 LogFlowFuncEnter();
234
235 PSHCLCONTEXT pCtx = pClient->State.pCtx;
236 AssertPtr(pCtx);
237
238 /* Drop the reference to the client, in case it is still there. This
239 * will cause any outstanding clipboard data requests from X11 to fail
240 * immediately. */
241 pCtx->fShuttingDown = true;
242
243 int rc = ShClX11ThreadStop(&pCtx->X11);
244 /** @todo handle this slightly more reasonably, or be really sure
245 * it won't go wrong. */
246 AssertRC(rc);
247
248 ShClX11Destroy(&pCtx->X11);
249 RTCritSectDelete(&pCtx->CritSect);
250
251 RTMemFree(pCtx);
252
253 /* Decrease active connections count. */
254 ASMAtomicDecU32(&g_cShClConnections);
255
256 LogFlowFuncLeaveRC(rc);
257 return rc;
258}
259
260/**
261 * Reports clipboard formats to the host clipboard.
262 */
263int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
264{
265 RT_NOREF(pBackend);
266
267 int rc = ShClX11ReportFormatsToX11Async(&pClient->State.pCtx->X11, fFormats);
268
269 LogFlowFuncLeaveRC(rc);
270 return rc;
271}
272
273/**
274 * Reads data from the host clipboard.
275 *
276 * Schedules a request to the X11 event thread.
277 *
278 * @note We always fail or complete asynchronously.
279 */
280int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
281 void *pvData, uint32_t cbData, uint32_t *pcbActual)
282{
283 RT_NOREF(pBackend);
284
285 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
286 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
287 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
288 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
289
290 RT_NOREF(pCmdCtx);
291
292 LogFlowFunc(("pClient=%p, uFormat=%#x, pv=%p, cb=%RU32, pcbActual=%p\n",
293 pClient, uFormat, pvData, cbData, pcbActual));
294
295 PSHCLEVENT pEvent;
296 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
297 if (RT_SUCCESS(rc))
298 {
299 rc = ShClX11ReadDataFromX11Async(&pClient->State.pCtx->X11, uFormat, cbData, pEvent);
300 if (RT_SUCCESS(rc))
301 {
302 int rcEvent;
303 PSHCLEVENTPAYLOAD pPayload;
304 rc = ShClEventWaitEx(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &rcEvent, &pPayload);
305 if (RT_SUCCESS(rc))
306 {
307 if (pPayload)
308 {
309 Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
310 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
311
312 uint32_t const cbRead = pResp->Read.cbData;
313
314 size_t const cbToCopy = RT_MIN(cbData, cbRead);
315 if (cbToCopy) /* memcpy doesn't like 0 byte inputs. */
316 memcpy(pvData, pResp->Read.pvData, RT_MIN(cbData, cbRead));
317
318 LogRel2(("Shared Clipboard: Read %RU32 bytes host X11 clipboard data\n", cbRead));
319
320 *pcbActual = cbRead;
321
322 RTMemFree(pResp->Read.pvData);
323 pResp->Read.cbData = 0;
324
325 ShClPayloadFree(pPayload);
326 }
327 else /* No payload given; could happen on invalid / not-expected formats. */
328 *pcbActual = 0;
329 }
330 else if (rc == VERR_SHCLPB_EVENT_FAILED)
331 rc = rcEvent;
332 }
333
334 ShClEventRelease(pEvent);
335 }
336
337 if (RT_FAILURE(rc))
338 LogRel(("Shared Clipboard: Error reading host clipboard data from X11, rc=%Rrc\n", rc));
339
340 LogFlowFuncLeaveRC(rc);
341 return rc;
342}
343
344int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
345 SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
346{
347 RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
348
349 LogFlowFuncEnter();
350
351 /* Nothing to do here yet. */
352
353 LogFlowFuncLeave();
354 return VINF_SUCCESS;
355}
356
357/**
358 * @copydoc SHCLCALLBACKS::pfnReportFormats
359 *
360 * Reports clipboard formats to the guest.
361 */
362static DECLCALLBACK(int) shClSvcX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
363{
364 RT_NOREF(pvUser);
365
366 LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
367
368 int rc = VINF_SUCCESS;
369 PSHCLCLIENT pClient = pCtx->pClient;
370 AssertPtr(pClient);
371
372 rc = ShClSvcReportFormats(pClient, fFormats);
373
374 LogFlowFuncLeaveRC(rc);
375 return rc;
376}
377
378#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
379/**
380 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCreated
381 *
382 * @thread Service main thread.
383 */
384static DECLCALLBACK(void) shClSvcX11TransferOnCreatedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
385{
386 LogFlowFuncEnter();
387
388 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
389 AssertPtr(pCtx);
390
391 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
392 AssertPtr(pTransfer);
393
394 PSHCLCLIENT const pClient = pCtx->pClient;
395 AssertPtr(pClient);
396
397 /*
398 * Set transfer provider.
399 * Those will be registered within ShClSvcTransferInit() when a new transfer gets initialized.
400 */
401
402 /* Set the interface to the local provider by default first. */
403 RT_ZERO(pClient->Transfers.Provider);
404 ShClTransferProviderLocalQueryInterface(&pClient->Transfers.Provider);
405
406 PSHCLTXPROVIDERIFACE pIface = &pClient->Transfers.Provider.Interface;
407
408 pClient->Transfers.Provider.enmSource = pClient->State.enmSource;
409 pClient->Transfers.Provider.pvUser = pClient;
410
411 switch (ShClTransferGetDir(pTransfer))
412 {
413 case SHCLTRANSFERDIR_FROM_REMOTE: /* Guest -> Host. */
414 {
415 pIface->pfnRootListRead = shClSvcTransferIfaceGHRootListRead;
416
417 pIface->pfnListOpen = shClSvcTransferIfaceGHListOpen;
418 pIface->pfnListClose = shClSvcTransferIfaceGHListClose;
419 pIface->pfnListHdrRead = shClSvcTransferIfaceGHListHdrRead;
420 pIface->pfnListEntryRead = shClSvcTransferIfaceGHListEntryRead;
421
422 pIface->pfnObjOpen = shClSvcTransferIfaceGHObjOpen;
423 pIface->pfnObjClose = shClSvcTransferIfaceGHObjClose;
424 pIface->pfnObjRead = shClSvcTransferIfaceGHObjRead;
425 break;
426 }
427
428 case SHCLTRANSFERDIR_TO_REMOTE: /* Host -> Guest. */
429 {
430 pIface->pfnRootListRead = shClSvcX11TransferIfaceHGRootListRead;
431 break;
432 }
433
434 default:
435 AssertFailed();
436 }
437
438 int rc = ShClTransferSetProvider(pTransfer, &pClient->Transfers.Provider); RT_NOREF(rc);
439
440 LogFlowFuncLeaveRC(rc);
441}
442
443/**
444 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
445 *
446 * For G->H: Starts the HTTP server if not done yet and registers the transfer with it.
447 * For H->G: Called on transfer intialization to populate the transfer's root list.
448 *
449 * @thread Service main thread.
450 */
451static DECLCALLBACK(void) shClSvcX11TransferOnInitCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
452{
453 LogFlowFuncEnter();
454
455# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
456 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
457 AssertPtr(pCtx);
458# endif
459
460 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
461 AssertPtr(pTransfer);
462
463 int rc;
464
465 switch (ShClTransferGetDir(pTransfer))
466 {
467 case SHCLTRANSFERDIR_FROM_REMOTE: /* G->H */
468 {
469# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
470 /* We only need to start the HTTP server when we actually receive data from the remote (host). */
471 rc = ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
472# endif
473 break;
474 }
475
476 case SHCLTRANSFERDIR_TO_REMOTE: /* H->G */
477 {
478 rc = ShClTransferRootListRead(pTransfer); /* Calls shClSvcX11TransferIfaceHGRootListRead(). */
479 break;
480 }
481
482 default:
483 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
484 break;
485 }
486
487 LogFlowFuncLeaveRC(rc);
488}
489
490/**
491 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnDestroy
492 *
493 * This stops the HTTP server if not done yet.
494 *
495 * @thread Service main thread.
496 */
497static DECLCALLBACK(void) shClSvcX11TransferOnDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
498{
499 LogFlowFuncEnter();
500
501# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
502 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
503 AssertPtr(pCtx);
504
505 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
506 AssertPtr(pTransfer);
507
508 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
509 ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
510# else
511 RT_NOREF(pCbCtx);
512# endif
513
514 LogFlowFuncLeave();
515}
516
517/**
518 * Unregisters a transfer from a HTTP server.
519 *
520 * This also stops the HTTP server if no active transfers are found anymore.
521 *
522 * @param pCtx Shared clipboard context to unregister transfer for.
523 * @param pTransfer Transfer to unregister.
524 *
525 * @thread Clipboard main thread.
526 */
527static void shClSvcX11HttpTransferUnregister(PSHCLCONTEXT pCtx, PSHCLTRANSFER pTransfer)
528{
529 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE)
530 {
531# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
532 if (ShClTransferHttpServerIsInitialized(&pCtx->X11.HttpCtx.HttpServer))
533 {
534 ShClTransferHttpServerUnregisterTransfer(&pCtx->X11.HttpCtx.HttpServer, pTransfer);
535 ShClTransferHttpServerMaybeStop(&pCtx->X11.HttpCtx);
536 }
537# else
538 RT_NOREF(pCtx);
539# endif
540 }
541
542 //ShClTransferRelease(pTransfer);
543}
544
545/**
546 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnUnregistered
547 *
548 * Unregisters a (now) unregistered transfer from the HTTP server.
549 *
550 * @thread Clipboard main thread.
551 */
552static DECLCALLBACK(void) shClSvcX11TransferOnUnregisteredCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, PSHCLTRANSFERCTX pTransferCtx)
553{
554 RT_NOREF(pTransferCtx);
555 shClSvcX11HttpTransferUnregister((PSHCLCONTEXT)pCbCtx->pvUser, pCbCtx->pTransfer);
556}
557#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
558
559/**
560 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
561 *
562 * Requests clipboard data from the guest.
563 *
564 * @thread Called from X11 event thread.
565 */
566static DECLCALLBACK(int) shClSvcX11RequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
567 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
568{
569 RT_NOREF(pvUser);
570
571 LogFlowFunc(("pCtx=%p, uFmt=0x%x\n", pCtx, uFmt));
572
573 if (pCtx->fShuttingDown)
574 {
575 /* The shared clipboard is disconnecting. */
576 LogRel(("Shared Clipboard: Host requested guest clipboard data after guest had disconnected\n"));
577 return VERR_WRONG_ORDER;
578 }
579
580 PSHCLCLIENT const pClient = pCtx->pClient;
581 int rc = ShClSvcReadDataFromGuest(pClient, uFmt, ppv, pcb);
582 if (RT_FAILURE(rc))
583 return rc;
584
585 /*
586 * Note: We always return a generic URI list (as HTTP links) here.
587 * As we don't know which Atom target format was requested by the caller, the X11 clipboard codes needs
588 * to decide & transform the list into the actual clipboard Atom target format the caller wanted.
589 */
590#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
591 if (uFmt == VBOX_SHCL_FMT_URI_LIST)
592 {
593 PSHCLTRANSFER pTransfer;
594 rc = ShClSvcTransferCreate(pClient, SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
595 NIL_SHCLTRANSFERID /* Creates a new transfer ID */, &pTransfer);
596 if (RT_SUCCESS(rc))
597 {
598 /* Initialize the transfer on the host side. */
599 rc = ShClSvcTransferInit(pClient, pTransfer);
600 if (RT_FAILURE(rc))
601 ShClSvcTransferDestroy(pClient, pTransfer);
602 }
603
604 if (RT_SUCCESS(rc))
605 {
606 /* We have to wait for the guest reporting the transfer as being initialized.
607 * Only then we can start reading stuff. */
608 rc = ShClTransferWaitForStatus(pTransfer, SHCL_TIMEOUT_DEFAULT_MS, SHCLTRANSFERSTATUS_INITIALIZED);
609 if (RT_SUCCESS(rc))
610 {
611 rc = ShClTransferRootListRead(pTransfer);
612 if (RT_SUCCESS(rc))
613 {
614# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
615 /* As soon as we register the transfer with the HTTP server, the transfer needs to have its roots set. */
616 PSHCLHTTPSERVER const pHttpSrv = &pCtx->X11.HttpCtx.HttpServer;
617 rc = ShClTransferHttpServerRegisterTransfer(pHttpSrv, pTransfer);
618 if (RT_SUCCESS(rc))
619 {
620 char *pszURL = NULL;
621
622 uint64_t const cRoots = ShClTransferRootsCount(pTransfer);
623 for (uint32_t i = 0; i < cRoots; i++)
624 {
625 char *pszEntry = ShClTransferHttpServerGetUrlA(pHttpSrv, ShClTransferGetID(pTransfer), i /* Entry index */);
626 AssertPtrBreakStmt(pszEntry, rc = VERR_NO_MEMORY);
627
628 if (i > 0)
629 {
630 rc = RTStrAAppend(&pszURL, "\n"); /* Separate entries with a newline. */
631 AssertRCBreak(rc);
632 }
633
634 rc = RTStrAAppend(&pszURL, pszEntry);
635 AssertRCBreak(rc);
636
637 RTStrFree(pszEntry);
638 }
639
640 if (RT_SUCCESS(rc))
641 {
642 *ppv = pszURL;
643 *pcb = strlen(pszURL) + 1 /* Include terminator */;
644
645 LogFlowFunc(("URL is '%s'\n", pszURL));
646
647 /* ppv has ownership of pszURL. */
648 }
649 }
650# else
651 rc = VERR_NOT_SUPPORTED;
652# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
653 }
654 }
655 }
656 }
657#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
658
659 if (RT_FAILURE(rc))
660 LogRel(("Shared Clipboard: Requesting X11 data in format %#x from guest failed with %Rrc\n", uFmt, rc));
661
662 LogFlowFuncLeaveRC(rc);
663 return rc;
664}
665
666#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
667/**
668 * Handles transfer status replies from the guest.
669 */
670int ShClBackendTransferHandleStatusReply(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLSOURCE enmSource, SHCLTRANSFERSTATUS enmStatus, int rcStatus)
671{
672 RT_NOREF(pBackend, pClient, enmSource, rcStatus);
673
674 PSHCLCONTEXT pCtx = pClient->State.pCtx; RT_NOREF(pCtx);
675
676 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE) /* Guest -> Host */
677 {
678 switch (enmStatus)
679 {
680 case SHCLTRANSFERSTATUS_INITIALIZED:
681 {
682#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
683 int rc2 = ShClTransferHttpServerMaybeStart(&pCtx->X11.HttpCtx);
684 if (RT_SUCCESS(rc2))
685 {
686
687 }
688
689 if (RT_FAILURE(rc2))
690 LogRel(("Shared Clipboard: Registering HTTP transfer failed: %Rrc\n", rc2));
691#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP */
692 break;
693 }
694
695 default:
696 break;
697 }
698 }
699
700 return VINF_SUCCESS;
701}
702
703
704/*********************************************************************************************************************************
705* Provider interface implementation *
706*********************************************************************************************************************************/
707
708/** @copydoc SHCLTXPROVIDERIFACE::pfnRootListRead */
709static DECLCALLBACK(int) shClSvcX11TransferIfaceHGRootListRead(PSHCLTXPROVIDERCTX pCtx)
710{
711 LogFlowFuncEnter();
712
713 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
714 AssertPtr(pClient);
715
716 AssertPtr(pClient->State.pCtx);
717 PSHCLX11CTX pX11 = &pClient->State.pCtx->X11;
718
719 PSHCLEVENT pEvent;
720 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
721 if (RT_SUCCESS(rc))
722 {
723 rc = ShClX11ReadDataFromX11Async(pX11, VBOX_SHCL_FMT_URI_LIST, UINT32_MAX, pEvent);
724 if (RT_SUCCESS(rc))
725 {
726 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
727 int rcEvent;
728 PSHCLEVENTPAYLOAD pPayload;
729 rc = ShClEventWaitEx(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &rcEvent, &pPayload);
730 if (RT_SUCCESS(rc))
731 {
732 if (pPayload)
733 {
734 Assert(pPayload->cbData == sizeof(SHCLX11RESPONSE));
735 AssertPtr(pPayload->pvData);
736 PSHCLX11RESPONSE pResp = (PSHCLX11RESPONSE)pPayload->pvData;
737
738 rc = ShClTransferRootsInitFromStringList(pCtx->pTransfer,
739 (char *)pResp->Read.pvData, pResp->Read.cbData + 1 /* Include zero terminator */);
740 if (RT_SUCCESS(rc))
741 LogRel2(("Shared Clipboard: Host reported %RU64 X11 root entries for transfer to guest\n",
742 ShClTransferRootsCount(pCtx->pTransfer)));
743
744 RTMemFree(pResp->Read.pvData);
745 pResp->Read.cbData = 0;
746
747 ShClPayloadFree(pPayload);
748 pPayload = NULL;
749 }
750 else /* No payload given; could happen on invalid / not-expected formats. */
751 rc = VERR_NO_DATA;
752 }
753 else if (rc == VERR_SHCLPB_EVENT_FAILED)
754 rc = rcEvent;
755 }
756
757 ShClEventRelease(pEvent);
758 }
759 else
760 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
761
762 LogFlowFuncLeaveRC(rc);
763 return rc;
764}
765#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
766
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette