VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers-http.cpp@ 100566

Last change on this file since 100566 was 100367, checked in by vboxsync, 18 months ago

Shared Clipboard: More work on making the internal APIs more fine grained and easier to follow. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.1 KB
Line 
1/* $Id: clipboard-transfers-http.cpp 100367 2023-07-04 16:23:18Z vboxsync $ */
2/** @file
3 * Shared Clipboard: HTTP server implementation for Shared Clipboard transfers on UNIX-y guests / hosts.
4 */
5
6/*
7 * Copyright (C) 2020-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#include <signal.h>
33
34#include <iprt/http.h>
35#include <iprt/http-server.h>
36
37#include <iprt/net.h> /* To make use of IPv4Addr in RTGETOPTUNION. */
38
39#include <iprt/asm.h>
40#include <iprt/assert.h>
41#include <iprt/ctype.h>
42#include <iprt/errcore.h>
43#include <iprt/file.h>
44#include <iprt/getopt.h>
45#include <iprt/initterm.h>
46#include <iprt/list.h>
47#include <iprt/mem.h>
48#include <iprt/message.h>
49#include <iprt/path.h>
50#include <iprt/rand.h>
51#include <iprt/semaphore.h>
52#include <iprt/stream.h>
53#include <iprt/string.h>
54#include <iprt/thread.h>
55#include <iprt/uuid.h>
56#include <iprt/vfs.h>
57
58#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
59#include <iprt/log.h>
60
61#include <VBox/HostServices/VBoxClipboardSvc.h>
62#include <VBox/GuestHost/SharedClipboard-x11.h>
63#include <VBox/GuestHost/SharedClipboard-transfers.h>
64
65
66/*********************************************************************************************************************************
67* Definitations *
68*********************************************************************************************************************************/
69
70#ifdef DEBUG_andy
71/** When enabled, this lets the HTTP server run at a predictable URL and port for debugging:
72 * URL: http://localhost:49200/transfer<ID> */
73# define VBOX_SHCL_DEBUG_HTTPSERVER
74#endif
75
76typedef struct _SHCLHTTPSERVERTRANSFER
77{
78 /** The node list. */
79 RTLISTNODE Node;
80 /** Pointer to associated transfer. */
81 PSHCLTRANSFER pTransfer;
82 /** The (cached) root list of the transfer. NULL if not cached yet. */
83 PSHCLLIST pRootList;
84 /** Critical section for serializing access. */
85 RTCRITSECT CritSect;
86 /** The handle we're going to use for this HTTP transfer. */
87 SHCLOBJHANDLE hObj;
88 /** The virtual path of the HTTP server's root directory for this transfer.
89 * Always has to start with a "/". */
90 char szPathVirtual[RTPATH_MAX];
91} SHCLHTTPSERVERTRANSFER;
92typedef SHCLHTTPSERVERTRANSFER *PSHCLHTTPSERVERTRANSFER;
93
94
95/*********************************************************************************************************************************
96* Prototypes *
97*********************************************************************************************************************************/
98static int shClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pThis);
99static const char *shClTransferHttpServerGetHost(PSHCLHTTPSERVER pSrv);
100static int shClTransferHttpServerDestroyTransfer(PSHCLHTTPSERVER pSrv, PSHCLHTTPSERVERTRANSFER pSrvTx);
101static SHCLHTTPSERVERSTATUS shclTransferHttpServerSetStatusLocked(PSHCLHTTPSERVER pSrv, SHCLHTTPSERVERSTATUS fStatus);
102
103
104/*********************************************************************************************************************************
105* Static assets *
106*********************************************************************************************************************************/
107
108static char s_shClHttpServerPage404[] = " \
109<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \
110 \"http://www.w3.org/TR/html4/strict.dtd\"> \
111<html> \
112 <head> \
113 <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"> \
114 <title>VirtualBox Shared Clipboard</title> \
115 </head> \
116 <body> \
117 <h1>VirtualBox Shared Clipboard</h1> \
118 <p>Error: 404</p> \
119 <p>Message: Entry not found.</p> \
120 </body> \
121</html>";
122
123
124/*********************************************************************************************************************************
125* Internal Shared Clipboard HTTP transfer functions *
126*********************************************************************************************************************************/
127
128/**
129 * Locks the critical section of a Shared Clipboard HTTP server instance.
130 *
131 * @param pSrv Shared Clipboard HTTP server instance to lock.
132 */
133DECLINLINE(void) shClTransferHttpServerLock(PSHCLHTTPSERVER pSrv)
134{
135 int rc2 = RTCritSectEnter(&pSrv->CritSect);
136 AssertRC(rc2);
137}
138
139/**
140 * Unlocks the critical section of a Shared Clipboard HTTP server instance.
141 *
142 * @param pSrv Shared Clipboard HTTP server instance to unlock.
143 */
144DECLINLINE(void) shClTransferHttpServerUnlock(PSHCLHTTPSERVER pSrv)
145{
146 int rc2 = RTCritSectLeave(&pSrv->CritSect);
147 AssertRC(rc2);
148}
149
150/**
151 * Locks an HTTP transfer.
152 *
153 * @param pSrvTx HTTP transfer to lock.
154 */
155DECLINLINE(void) shClHttpTransferLock(PSHCLHTTPSERVERTRANSFER pSrvTx)
156{
157 int rc2 = RTCritSectEnter(&pSrvTx->CritSect);
158 AssertRC(rc2);
159}
160
161/**
162 * Unlocks an HTTP transfer.
163 *
164 * @param pSrvTx HTTP transfer to unlock.
165 */
166DECLINLINE(void) shClHttpTransferUnlock(PSHCLHTTPSERVERTRANSFER pSrvTx)
167{
168 int rc2 = RTCritSectLeave(&pSrvTx->CritSect);
169 AssertRC(rc2);
170}
171
172/**
173 * Return the HTTP server transfer for a specific transfer ID.
174 *
175 * @returns Pointer to HTTP server transfer if found, NULL if not found.
176 * @param pSrv HTTP server instance.
177 * @param idTransfer Transfer ID to return HTTP server transfer for.
178 */
179static PSHCLHTTPSERVERTRANSFER shClTransferHttpServerGetTransferById(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
180{
181 PSHCLHTTPSERVERTRANSFER pSrvTx;
182 RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node) /** @todo Slow O(n) lookup, but does it for now. */
183 {
184 if (pSrvTx->pTransfer->State.uID == idTransfer)
185 return pSrvTx;
186 }
187
188 return NULL;
189}
190
191/**
192 * Returns a HTTP server transfer from a given URL.
193 *
194 * @returns Pointer to HTTP server transfer if found, NULL if not found.
195 * @param pThis HTTP server instance data.
196 * @param pszUrl URL to validate.
197 */
198DECLINLINE(PSHCLHTTPSERVERTRANSFER) shClTransferHttpGetTransferFromUrl(PSHCLHTTPSERVER pThis, const char *pszUrl)
199{
200 AssertPtrReturn(pszUrl, NULL);
201
202 PSHCLHTTPSERVERTRANSFER pSrvTx = NULL;
203
204 PSHCLHTTPSERVERTRANSFER pSrvTxCur;
205 RTListForEach(&pThis->lstTransfers, pSrvTxCur, SHCLHTTPSERVERTRANSFER, Node)
206 {
207 AssertPtr(pSrvTxCur->pTransfer);
208
209 LogFlowFunc(("pSrvTxCur=%s\n", pSrvTxCur->szPathVirtual));
210
211 /* Be picky here, do a case sensitive comparison. */
212 if (RTStrStartsWith(pszUrl, pSrvTxCur->szPathVirtual))
213 {
214 pSrvTx = pSrvTxCur;
215 break;
216 }
217 }
218
219 if (!pSrvTx)
220 LogRel2(("Shared Clipboard: HTTP URL '%s' not valid\n", pszUrl));
221
222 LogFlowFunc(("pszUrl=%s, pSrvTx=%p\n", pszUrl, pSrvTx));
223 return pSrvTx;
224}
225
226/**
227 * Returns a HTTP server transfer from an internal HTTP handle.
228 *
229 * @returns Pointer to HTTP server transfer if found, NULL if not found.
230 * @param pThis HTTP server instance data.
231 * @param pvHandle Handle to return transfer for.
232 */
233DECLINLINE(PSHCLHTTPSERVERTRANSFER) shClTransferHttpGetTransferFromHandle(PSHCLHTTPSERVER pThis, void *pvHandle)
234{
235 AssertPtrReturn(pvHandle, NULL);
236
237 const SHCLTRANSFERID uHandle = *(uint16_t *)pvHandle;
238
239 /** @ŧodo Use a handle lookup table (map) later. */
240 PSHCLHTTPSERVERTRANSFER pSrvTxCur;
241 RTListForEach(&pThis->lstTransfers, pSrvTxCur, SHCLHTTPSERVERTRANSFER, Node)
242 {
243 AssertPtr(pSrvTxCur->pTransfer);
244
245 if (pSrvTxCur->pTransfer->State.uID == uHandle) /** @ŧodo We're using the transfer ID as handle for now. */
246 return pSrvTxCur;
247 }
248
249 return NULL;
250}
251
252
253/*********************************************************************************************************************************
254* HTTP server callback implementations *
255*********************************************************************************************************************************/
256
257/** @copydoc RTHTTPSERVERCALLBACKS::pfnRequestBegin */
258static DECLCALLBACK(int) shClTransferHttpBegin(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq)
259{
260 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser; RT_NOREF(pThis);
261 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
262
263 LogRel2(("Shared Clipboard: HTTP request begin\n"));
264
265 PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpGetTransferFromUrl(pThis, pReq->pszUrl);
266 if (pSrvTx)
267 {
268 pReq->pvUser = pSrvTx;
269 }
270
271 return VINF_SUCCESS;
272}
273
274/** @copydoc RTHTTPSERVERCALLBACKS::pfnRequestEnd */
275static DECLCALLBACK(int) shClTransferHttpEnd(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq)
276{
277 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser; RT_NOREF(pThis);
278 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
279
280 LogRel2(("Shared Clipboard: HTTP request end\n"));
281
282 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
283 if (pSrvTx)
284 {
285 pReq->pvUser = NULL;
286 }
287
288 return VINF_SUCCESS;
289
290}
291
292/** @copydoc RTHTTPSERVERCALLBACKS::pfnOpen */
293static DECLCALLBACK(int) shClTransferHttpOpen(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void **ppvHandle)
294{
295 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser; RT_NOREF(pThis);
296 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
297
298 int rc;
299
300 AssertPtr(pReq->pvUser);
301 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
302 if (pSrvTx)
303 {
304 LogRel2(("Shared Clipboard: HTTP transfer (handle %RU64) started ...\n", pSrvTx->hObj));
305
306 Assert(pSrvTx->hObj != NIL_SHCLOBJHANDLE);
307 *ppvHandle = &pSrvTx->hObj;
308 }
309 else
310 rc = VERR_NOT_FOUND;
311
312 if (RT_FAILURE(rc))
313 LogRel(("Shared Clipboard: Error starting HTTP transfer for '%s', rc=%Rrc\n", pReq->pszUrl, rc));
314
315 LogFlowFuncLeaveRC(rc);
316 return rc;
317}
318
319/** @copydoc RTHTTPSERVERCALLBACKS::pfnRead */
320static DECLCALLBACK(int) shClTransferHttpRead(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq,
321 void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead)
322{
323 RT_NOREF(pData);
324
325 if (pvHandle == NULL) /* Serve a 404 page if we got an invalid handle. */
326 {
327 Assert(cbBuf >= sizeof(s_shClHttpServerPage404)); /* Keep it simple for now. */
328 memcpy(pvBuf, &s_shClHttpServerPage404, RT_MIN(cbBuf, sizeof(s_shClHttpServerPage404)));
329 *pcbRead = sizeof(s_shClHttpServerPage404);
330 return VINF_SUCCESS;
331 }
332
333 int rc;
334
335 LogRel3(("Shared Clipboard: Reading %RU32 bytes from HTTP ...\n", cbBuf));
336
337 AssertPtr(pReq->pvUser);
338 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
339 if (pSrvTx)
340 {
341 PSHCLOBJHANDLE phObj = (PSHCLOBJHANDLE)pvHandle;
342 if (phObj)
343 {
344 uint32_t cbRead;
345 rc = ShClTransferObjRead(pSrvTx->pTransfer, *phObj, pvBuf, cbBuf, 0 /* fFlags */, &cbRead);
346 if (RT_SUCCESS(rc))
347 *pcbRead = (uint32_t)cbRead;
348
349 if (RT_FAILURE(rc))
350 LogRel(("Shared Clipboard: Error reading HTTP transfer (handle %RU64), rc=%Rrc\n", *phObj, rc));
351 }
352 else
353 rc = VERR_NOT_FOUND;
354 }
355 else
356 rc = VERR_NOT_FOUND;
357
358 LogFlowFuncLeaveRC(rc);
359 return rc;
360}
361
362/** @copydoc RTHTTPSERVERCALLBACKS::pfnClose */
363static DECLCALLBACK(int) shClTransferHttpClose(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void *pvHandle)
364{
365 RT_NOREF(pData);
366
367 int rc;
368
369 AssertPtr(pReq->pvUser);
370 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
371 if (pSrvTx)
372 {
373 PSHCLOBJHANDLE phObj = (PSHCLOBJHANDLE)pvHandle;
374 if (phObj)
375 {
376 Assert(*phObj != NIL_SHCLOBJHANDLE);
377 rc = ShClTransferObjClose(pSrvTx->pTransfer, *phObj);
378 if (RT_SUCCESS(rc))
379 {
380 pSrvTx->hObj = NIL_SHCLOBJHANDLE;
381 LogRel2(("Shared Clipboard: HTTP transfer %RU16 done\n", pSrvTx->pTransfer->State.uID));
382 }
383
384 if (RT_FAILURE(rc))
385 LogRel(("Shared Clipboard: Error closing HTTP transfer (handle %RU64), rc=%Rrc\n", *phObj, rc));
386 }
387 else
388 rc = VERR_NOT_FOUND;
389 }
390 else
391 rc = VERR_NOT_FOUND;
392
393 LogFlowFuncLeaveRC(rc);
394 return rc;
395}
396
397/** @copydoc RTHTTPSERVERCALLBACKS::pfnQueryInfo */
398static DECLCALLBACK(int) shClTransferHttpQueryInfo(PRTHTTPCALLBACKDATA pData,
399 PRTHTTPSERVERREQ pReq, PRTFSOBJINFO pObjInfo, char **ppszMIMEHint)
400{
401 RT_NOREF(pData);
402 RT_NOREF(ppszMIMEHint);
403
404 int rc;
405
406 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)pReq->pvUser;
407 if (pSrvTx)
408 {
409 SHCLOBJOPENCREATEPARMS openParms;
410 rc = ShClTransferObjOpenParmsInit(&openParms);
411 if (RT_SUCCESS(rc))
412 {
413 openParms.fCreate = SHCL_OBJ_CF_ACCESS_READ
414 | SHCL_OBJ_CF_ACCESS_DENYWRITE;
415
416 PSHCLTRANSFER pTx = pSrvTx->pTransfer;
417 AssertPtr(pTx);
418
419 /** @todo For now we only serve single files, hence index 0 below. */
420 PCSHCLLISTENTRY pEntry = ShClTransferRootsEntryGet(pTx, 0 /* First file */);
421 if (pEntry)
422 {
423 LogRel2(("Shared Clipboard: Querying HTTP transfer information for '%s' ...\n", pEntry->pszName));
424
425 rc = RTStrCopy(openParms.pszPath, openParms.cbPath, pEntry->pszName);
426 if (RT_SUCCESS(rc))
427 {
428 rc = ShClTransferObjOpen(pTx, &openParms, &pSrvTx->hObj);
429 if (RT_SUCCESS(rc))
430 {
431 rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
432
433 if ( pEntry->fInfo & VBOX_SHCL_INFO_F_FSOBJINFO
434 && pEntry->cbInfo == sizeof(SHCLFSOBJINFO))
435 {
436 PCSHCLFSOBJINFO pSrcObjInfo = (PSHCLFSOBJINFO)pEntry->pvInfo;
437
438 LogFlowFunc(("pszName=%s, cbInfo=%RU32, fMode=%#x (type %#x)\n",
439 pEntry->pszName, pEntry->cbInfo, pSrcObjInfo->Attr.fMode, (pSrcObjInfo->Attr.fMode & RTFS_TYPE_MASK)));
440
441 if (RTFS_IS_FILE(pSrcObjInfo->Attr.fMode))
442 {
443 memcpy(pObjInfo, pSrcObjInfo, sizeof(SHCLFSOBJINFO));
444 rc = VINF_SUCCESS;
445 }
446 }
447 else
448 LogRel2(("Shared Clipboard: Supplied entry information for '%s' not supported (fInfo=%#x, cbInfo=%RU32\n",
449 pEntry->pszName, pEntry->fInfo, pEntry->cbInfo));
450 }
451 }
452 }
453 else
454 rc = VERR_NOT_FOUND;
455
456 ShClTransferObjOpenParmsDestroy(&openParms);
457 }
458 }
459 else
460 rc = VERR_NOT_FOUND;
461
462 if (RT_FAILURE(rc))
463 LogRel(("Shared Clipboard: Querying info for HTTP transfer failed with %Rrc\n", rc));
464
465 LogFlowFuncLeaveRC(rc);
466 return rc;
467}
468
469
470/*********************************************************************************************************************************
471* Internal Shared Clipboard HTTP server functions *
472*********************************************************************************************************************************/
473
474/**
475 * Destroys a Shared Clipboard HTTP server instance, internal version.
476 *
477 * @returns VBox status code.
478 * @param pSrv Shared Clipboard HTTP server instance to destroy.
479 *
480 * @note Caller needs to take the critical section.
481 */
482static int shClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pSrv)
483{
484 Assert(RTCritSectIsOwner(&pSrv->CritSect));
485
486 LogFlowFuncEnter();
487
488 pSrv->fInitialized = false;
489 pSrv->fRunning = false;
490
491 int rc = VINF_SUCCESS;
492
493 PSHCLHTTPSERVERTRANSFER pSrvTx, pSrvTxNext;
494 RTListForEachSafe(&pSrv->lstTransfers, pSrvTx, pSrvTxNext, SHCLHTTPSERVERTRANSFER, Node)
495 {
496 int rc2 = shClTransferHttpServerDestroyTransfer(pSrv, pSrvTx);
497 if (RT_SUCCESS(rc))
498 rc = rc2;
499 }
500
501 RTHttpServerResponseDestroy(&pSrv->Resp);
502
503 pSrv->hHTTPServer = NIL_RTHTTPSERVER;
504
505 shClTransferHttpServerUnlock(pSrv); /* Unlock critical section taken by the caller before deleting it. */
506
507 if (RTCritSectIsInitialized(&pSrv->CritSect))
508 {
509 int rc2 = RTCritSectDelete(&pSrv->CritSect);
510 if (RT_SUCCESS(rc))
511 rc = rc2;
512 }
513
514 LogFlowFuncLeaveRC(rc);
515 return rc;
516}
517
518/**
519 * Initializes a new Shared Clipboard HTTP server instance.
520 *
521 * @return VBox status code.
522 * @param pSrv HTTP server instance to initialize.
523 */
524static int shClTransferHttpServerInitInternal(PSHCLHTTPSERVER pSrv)
525{
526 int rc = RTCritSectInit(&pSrv->CritSect);
527 AssertRCReturn(rc, rc);
528
529 rc = RTSemEventCreate(&pSrv->StatusEvent);
530 AssertRCReturn(rc, rc);
531
532 pSrv->hHTTPServer = NIL_RTHTTPSERVER;
533 pSrv->uPort = 0;
534 RTListInit(&pSrv->lstTransfers);
535 pSrv->cTransfers = 0;
536
537 rc = RTHttpServerResponseInit(&pSrv->Resp);
538 AssertRCReturn(rc, rc);
539
540 ASMAtomicXchgBool(&pSrv->fInitialized, true);
541 ASMAtomicXchgBool(&pSrv->fRunning, false);
542
543 return rc;
544}
545
546
547/*********************************************************************************************************************************
548* Public Shared Clipboard HTTP server functions *
549*********************************************************************************************************************************/
550
551/**
552 * Initializes a new Shared Clipboard HTTP server instance.
553 *
554 * @return VBox status code.
555 * @param pSrv HTTP server instance to initialize.
556 */
557int ShClTransferHttpServerInit(PSHCLHTTPSERVER pSrv)
558{
559 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
560
561 return shClTransferHttpServerInitInternal(pSrv);
562}
563
564/**
565 * Returns whether a given TCP port is known to be buggy or not.
566 *
567 * @returns \c true if the given port is known to be buggy, or \c false if not.
568 * @param uPort TCP port to check.
569 */
570static bool shClTransferHttpServerPortIsBuggy(uint16_t uPort)
571{
572 uint16_t const aBuggyPorts[] = {
573#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
574 /* GNOME Nautilus ("Files") v43 is unable download HTTP files from this port. */
575 8080
576#else /* Prevents zero-sized arrays. */
577 0
578#endif
579 };
580
581 for (size_t i = 0; i < RT_ELEMENTS(aBuggyPorts); i++)
582 if (uPort == aBuggyPorts[i])
583 return true;
584 return false;
585}
586
587/**
588 * Starts the Shared Clipboard HTTP server instance, extended version.
589 *
590 * @returns VBox status code.
591 * @return VERR_ADDRESS_CONFLICT if the port is already taken or the port is known to be buggy.
592 * @param pSrv HTTP server instance to create.
593 * @param uPort TCP port number to use.
594 */
595int ShClTransferHttpServerStartEx(PSHCLHTTPSERVER pSrv, uint16_t uPort)
596{
597 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
598 AssertReturn(uPort, VERR_INVALID_PARAMETER);
599
600 AssertReturn(!shClTransferHttpServerPortIsBuggy(uPort), VERR_ADDRESS_CONFLICT);
601
602 shClTransferHttpServerLock(pSrv);
603
604 RTHTTPSERVERCALLBACKS Callbacks;
605 RT_ZERO(Callbacks);
606
607 Callbacks.pfnRequestBegin = shClTransferHttpBegin;
608 Callbacks.pfnRequestEnd = shClTransferHttpEnd;
609 Callbacks.pfnOpen = shClTransferHttpOpen;
610 Callbacks.pfnRead = shClTransferHttpRead;
611 Callbacks.pfnClose = shClTransferHttpClose;
612 Callbacks.pfnQueryInfo = shClTransferHttpQueryInfo;
613
614 /* Note: The server always and *only* runs against the localhost interface. */
615 int rc = RTHttpServerCreate(&pSrv->hHTTPServer, "localhost", uPort, &Callbacks,
616 pSrv, sizeof(SHCLHTTPSERVER));
617 if (RT_SUCCESS(rc))
618 {
619 pSrv->uPort = uPort;
620 ASMAtomicXchgBool(&pSrv->fRunning, true);
621
622 LogRel2(("Shared Clipboard: HTTP server started at port %RU16\n", pSrv->uPort));
623
624 rc = shclTransferHttpServerSetStatusLocked(pSrv, SHCLHTTPSERVERSTATUS_STARTED);
625 }
626
627 shClTransferHttpServerUnlock(pSrv);
628
629 if (RT_FAILURE(rc))
630 LogRel(("Shared Clipboard: HTTP server failed to start, rc=%Rrc\n", rc));
631
632 return rc;
633}
634
635/**
636 * Translates a Shared Clipboard HTTP server status to a string.
637 *
638 * @returns Status as a string.
639 * @param uMsg Status to translate.
640 */
641static const char *shClTransferHttpServerStatusToStr(SHCLHTTPSERVERSTATUS enmStatus)
642{
643 switch (enmStatus)
644 {
645 RT_CASE_RET_STR(SHCLHTTPSERVERSTATUS_NONE);
646 RT_CASE_RET_STR(SHCLHTTPSERVERSTATUS_STARTED);
647 RT_CASE_RET_STR(SHCLHTTPSERVERSTATUS_STOPPED);
648 RT_CASE_RET_STR(SHCLHTTPSERVERSTATUS_TRANSFER_REGISTERED);
649 RT_CASE_RET_STR(SHCLHTTPSERVERSTATUS_TRANSFER_UNREGISTERED);
650 }
651
652 AssertFailedReturn("Unknown");
653}
654
655/**
656 * Starts the Shared Clipboard HTTP server instance using a random port (>= 49152).
657 *
658 * This does automatic probing of TCP ports if a port already is being used.
659 *
660 * @returns VBox status code.
661 * @param pSrv HTTP server instance to create.
662 * @param cMaxAttempts Maximum number of attempts to create a HTTP server.
663 * @param puPort Where to return the TCP port number being used on success. Optional.
664 *
665 * @note Complies with RFC 6335 (IANA).
666 */
667int ShClTransferHttpServerStart(PSHCLHTTPSERVER pSrv, unsigned cMaxAttempts, uint16_t *puPort)
668{
669 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
670 AssertReturn(cMaxAttempts, VERR_INVALID_PARAMETER);
671 /* puPort is optional. */
672
673 int rc;
674#ifdef VBOX_SHCL_DEBUG_HTTPSERVER
675 uint16_t uDebugPort = 49200;
676 rc = ShClTransferHttpServerStartEx(pSrv, (uint32_t)uDebugPort);
677 if (RT_SUCCESS(rc))
678 {
679 if (puPort)
680 *puPort = uDebugPort;
681 }
682 return rc;
683#endif
684
685 RTRAND hRand;
686 rc = RTRandAdvCreateSystemFaster(&hRand); /* Should be good enough for this task. */
687 if (RT_SUCCESS(rc))
688 {
689 uint16_t uPort;
690 unsigned i = 0;
691 for (i; i < cMaxAttempts; i++)
692 {
693 /* Try some random ports >= 49152 (i.e. "dynamic ports", see RFC 6335)
694 * -- required, as VBoxClient runs as a user process on the guest. */
695 uPort = RTRandAdvU32Ex(hRand, 49152, UINT16_MAX);
696
697 /* If the port selected turns is known to be buggy for whatever reason, skip it and try another one. */
698 if (shClTransferHttpServerPortIsBuggy(uPort))
699 continue;
700
701 rc = ShClTransferHttpServerStartEx(pSrv, (uint32_t)uPort);
702 if (RT_SUCCESS(rc))
703 {
704 if (puPort)
705 *puPort = uPort;
706 break;
707 }
708 }
709
710 if ( RT_FAILURE(rc)
711 && i == cMaxAttempts)
712 LogRel(("Shared Clipboard: Maximum attempts to start HTTP server reached (%u), giving up\n", cMaxAttempts));
713
714 RTRandAdvDestroy(hRand);
715 }
716
717 return rc;
718}
719
720/**
721 * Stops a Shared Clipboard HTTP server instance.
722 *
723 * @returns VBox status code.
724 * @param pSrv HTTP server instance to stop.
725 */
726int ShClTransferHttpServerStop(PSHCLHTTPSERVER pSrv)
727{
728 LogFlowFuncEnter();
729
730 shClTransferHttpServerLock(pSrv);
731
732 int rc = VINF_SUCCESS;
733
734 if (pSrv->fRunning)
735 {
736 Assert(pSrv->hHTTPServer != NIL_RTHTTPSERVER);
737
738 rc = RTHttpServerDestroy(pSrv->hHTTPServer);
739 if (RT_SUCCESS(rc))
740 {
741 pSrv->hHTTPServer = NIL_RTHTTPSERVER;
742 pSrv->fRunning = false;
743
744 /* Let any eventual waiters know. */
745 shclTransferHttpServerSetStatusLocked(pSrv, SHCLHTTPSERVERSTATUS_STOPPED);
746
747 LogRel2(("Shared Clipboard: HTTP server stopped\n"));
748 }
749 }
750
751 if (RT_FAILURE(rc))
752 LogRel(("Shared Clipboard: HTTP server failed to stop, rc=%Rrc\n", rc));
753
754 shClTransferHttpServerUnlock(pSrv);
755
756 LogFlowFuncLeaveRC(rc);
757 return rc;
758}
759
760/**
761 * Destroys a Shared Clipboard HTTP server instance.
762 *
763 * @returns VBox status code.
764 * @param pSrv HTTP server instance to destroy.
765 */
766int ShClTransferHttpServerDestroy(PSHCLHTTPSERVER pSrv)
767{
768 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
769
770 int rc = ShClTransferHttpServerStop(pSrv);
771 if (RT_FAILURE(rc))
772 return rc;
773
774 if (!ASMAtomicReadBool(&pSrv->fInitialized))
775 return VINF_SUCCESS;
776
777 shClTransferHttpServerLock(pSrv);
778
779 rc = shClTransferHttpServerDestroyInternal(pSrv);
780
781 /* Unlock not needed anymore, as the critical section got destroyed. */
782
783 return rc;
784}
785
786/**
787 * Returns the host name (scheme) of a HTTP server instance.
788 *
789 * @returns Host name (scheme).
790 * @param pSrv HTTP server instance to return host name (scheme) for.
791 *
792 * @note This is hardcoded to "localhost" for now.
793 */
794static const char *shClTransferHttpServerGetHost(PSHCLHTTPSERVER pSrv)
795{
796 RT_NOREF(pSrv);
797 return "http://localhost"; /* Hardcoded for now. */
798}
799
800/**
801 * Destroys a server transfer, internal version.
802 *
803 * @returns VBox status code.
804 * @param pSrv HTTP server instance to unregister transfer from.
805 * @param pTransfer Server transfer to destroy
806 * The pointer will be invalid on success.
807 *
808 * @note Caller needs to take the server critical section.
809 */
810static int shClTransferHttpServerDestroyTransfer(PSHCLHTTPSERVER pSrv, PSHCLHTTPSERVERTRANSFER pSrvTx)
811{
812 Assert(RTCritSectIsOwner(&pSrv->CritSect));
813
814 RTListNodeRemove(&pSrvTx->Node);
815
816 Assert(pSrv->cTransfers);
817 pSrv->cTransfers--;
818
819 LogFunc(("pTransfer=%p, idTransfer=%RU16, szPath=%s -> %RU32 transfers\n",
820 pSrvTx->pTransfer, pSrvTx->pTransfer->State.uID, pSrvTx->szPathVirtual, pSrv->cTransfers));
821
822 LogRel2(("Shared Clipboard: Destroyed HTTP transfer %RU16, now %RU32 HTTP transfers total\n",
823 pSrvTx->pTransfer->State.uID, pSrv->cTransfers));
824
825 if (RTCritSectIsInitialized(&pSrvTx->CritSect))
826 {
827 int rc = RTCritSectDelete(&pSrvTx->CritSect);
828 AssertRCReturn(rc, rc);
829 }
830
831 RTMemFree(pSrvTx);
832 pSrvTx = NULL;
833
834 return VINF_SUCCESS;
835}
836
837
838/*********************************************************************************************************************************
839* Public Shared Clipboard HTTP server functions *
840*********************************************************************************************************************************/
841
842/**
843 * Registers a Shared Clipboard transfer to a HTTP server instance.
844 *
845 * @returns VBox status code.
846 * @param pSrv HTTP server instance to register transfer for.
847 * @param pTransfer Transfer to register. Needs to be on the heap.
848 */
849int ShClTransferHttpServerRegisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
850{
851 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
852 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
853
854 AssertMsgReturn(pTransfer->State.uID, ("Transfer needs to be registered with a transfer context first\n"),
855 VERR_INVALID_PARAMETER);
856
857 uint64_t const cRoots = ShClTransferRootsCount(pTransfer);
858 AssertMsgReturn(cRoots > 0, ("Transfer has no root entries\n"), VERR_INVALID_PARAMETER);
859 AssertMsgReturn(cRoots == 1, ("Only single files are supported for now\n"), VERR_NOT_SUPPORTED);
860 /** @todo Check for directories? */
861
862 shClTransferHttpServerLock(pSrv);
863
864 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)RTMemAllocZ(sizeof(SHCLHTTPSERVERTRANSFER));
865 AssertPtrReturn(pSrvTx, VERR_NO_MEMORY);
866
867 RTUUID Uuid;
868 int rc = RTUuidCreate(&Uuid);
869 if (RT_SUCCESS(rc))
870 {
871 char szUuid[64];
872 rc = RTUuidToStr(&Uuid, szUuid, sizeof(szUuid));
873 if (RT_SUCCESS(rc))
874 {
875 rc = RTCritSectInit(&pSrvTx->CritSect);
876 AssertRCReturn(rc, rc);
877
878 PCSHCLLISTENTRY pEntry = ShClTransferRootsEntryGet(pTransfer, 0 /* First file */);
879 if (pEntry)
880 {
881 /* Create the virtual HTTP path for the transfer.
882 * Every transfer has a dedicated HTTP path (but live in the same URL namespace). */
883#ifdef VBOX_SHCL_DEBUG_HTTPSERVER
884# ifdef DEBUG_andy /** Too lazy to specify a different transfer ID for debugging. */
885 ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "/transfer");
886# else
887 ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "/transfer%RU16",
888 pTransfer->State.uID);
889# endif
890#else
891 ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "/%s/%s/%s",
892 SHCL_HTTPT_URL_NAMESPACE, szUuid, pEntry->pszName);
893#endif
894 AssertReturn(cch, VERR_BUFFER_OVERFLOW);
895
896 pSrvTx->pTransfer = pTransfer;
897 pSrvTx->pRootList = NULL;
898 pSrvTx->hObj = NIL_SHCLOBJHANDLE;
899
900 RTListAppend(&pSrv->lstTransfers, &pSrvTx->Node);
901 pSrv->cTransfers++;
902
903 shclTransferHttpServerSetStatusLocked(pSrv, SHCLHTTPSERVERSTATUS_TRANSFER_REGISTERED);
904
905 LogFunc(("pTransfer=%p, idTransfer=%RU16, szPath=%s -> %RU32 transfers\n",
906 pSrvTx->pTransfer, pSrvTx->pTransfer->State.uID, pSrvTx->szPathVirtual, pSrv->cTransfers));
907
908 LogRel2(("Shared Clipboard: Registered HTTP transfer %RU16, now %RU32 HTTP transfers total\n",
909 pTransfer->State.uID, pSrv->cTransfers));
910 }
911 }
912 }
913
914 if (RT_FAILURE(rc))
915 RTMemFree(pSrvTx);
916
917 shClTransferHttpServerUnlock(pSrv);
918
919 LogFlowFuncLeaveRC(rc);
920 return rc;
921}
922
923/**
924 * Unregisters a formerly registered Shared Clipboard transfer.
925 *
926 * @returns VBox status code.
927 * @param pSrv HTTP server instance to unregister transfer from.
928 * @param pTransfer Transfer to unregister.
929 */
930int ShClTransferHttpServerUnregisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
931{
932 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
933 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
934
935 shClTransferHttpServerLock(pSrv);
936
937 int rc = VINF_SUCCESS;
938
939 PSHCLHTTPSERVERTRANSFER pSrvTx, pSrvTxNext;
940 RTListForEachSafe(&pSrv->lstTransfers, pSrvTx, pSrvTxNext, SHCLHTTPSERVERTRANSFER, Node)
941 {
942 AssertPtr(pSrvTx->pTransfer);
943 if (pSrvTx->pTransfer->State.uID == pTransfer->State.uID)
944 {
945 rc = shClTransferHttpServerDestroyTransfer(pSrv, pSrvTx);
946 if (RT_SUCCESS(rc))
947 shclTransferHttpServerSetStatusLocked(pSrv, SHCLHTTPSERVERSTATUS_TRANSFER_UNREGISTERED);
948 break;
949 }
950 }
951
952 shClTransferHttpServerUnlock(pSrv);
953
954 LogFlowFuncLeaveRC(rc);
955 return rc;
956}
957
958/**
959 * Sets a new status.
960 *
961 * @returns New status set.
962 * @param pSrv HTTP server instance to set status for.
963 * @param fStatus New status to set.
964 *
965 * @note Caller needs to take critical section.
966 */
967static SHCLHTTPSERVERSTATUS shclTransferHttpServerSetStatusLocked(PSHCLHTTPSERVER pSrv, SHCLHTTPSERVERSTATUS enmStatus)
968{
969 Assert(RTCritSectIsOwner(&pSrv->CritSect));
970
971 /* Bogus checks. */
972 Assert(!(enmStatus & SHCLHTTPSERVERSTATUS_NONE) || enmStatus == SHCLHTTPSERVERSTATUS_NONE);
973
974 pSrv->enmStatus = enmStatus;
975 LogFlowFunc(("fStatus=%#x\n", pSrv->enmStatus));
976
977 int rc2 = RTSemEventSignal(pSrv->StatusEvent);
978 AssertRC(rc2);
979
980 return pSrv->enmStatus;
981}
982
983/**
984 * Returns the first transfer in the list.
985 *
986 * @returns Pointer to first transfer if found, or NULL if not found.
987 * @param pSrv HTTP server instance.
988 */
989PSHCLTRANSFER ShClTransferHttpServerGetTransferFirst(PSHCLHTTPSERVER pSrv)
990{
991 shClTransferHttpServerLock(pSrv);
992
993 PSHCLHTTPSERVERTRANSFER pHttpTransfer = RTListGetFirst(&pSrv->lstTransfers, SHCLHTTPSERVERTRANSFER, Node);
994
995 shClTransferHttpServerUnlock(pSrv);
996
997 return pHttpTransfer ? pHttpTransfer->pTransfer : NULL;
998}
999
1000/**
1001 * Returns the last transfer in the list.
1002 *
1003 * @returns Pointer to last transfer if found, or NULL if not found.
1004 * @param pSrv HTTP server instance.
1005 */
1006PSHCLTRANSFER ShClTransferHttpServerGetTransferLast(PSHCLHTTPSERVER pSrv)
1007{
1008 shClTransferHttpServerLock(pSrv);
1009
1010 PSHCLHTTPSERVERTRANSFER pHttpTransfer = RTListGetLast(&pSrv->lstTransfers, SHCLHTTPSERVERTRANSFER, Node);
1011
1012 shClTransferHttpServerUnlock(pSrv);
1013
1014 return pHttpTransfer ? pHttpTransfer->pTransfer : NULL;
1015}
1016
1017/**
1018 * Returns a transfer for a specific ID.
1019 *
1020 * @returns Pointer to the transfer if found, or NULL if not found.
1021 * @param pSrv HTTP server instance.
1022 * @param idTransfer Transfer ID of transfer to return..
1023 */
1024bool ShClTransferHttpServerGetTransfer(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
1025{
1026 AssertPtrReturn(pSrv, false);
1027
1028 shClTransferHttpServerLock(pSrv);
1029
1030 PSHCLHTTPSERVERTRANSFER pTransfer = shClTransferHttpServerGetTransferById(pSrv, idTransfer);
1031
1032 shClTransferHttpServerUnlock(pSrv);
1033
1034 return pTransfer;
1035}
1036
1037/**
1038 * Returns the used TCP port number of a HTTP server instance.
1039 *
1040 * @returns TCP port number. 0 if not specified yet.
1041 * @param pSrv HTTP server instance to return port for.
1042 */
1043uint16_t ShClTransferHttpServerGetPort(PSHCLHTTPSERVER pSrv)
1044{
1045 AssertPtrReturn(pSrv, 0);
1046
1047 shClTransferHttpServerLock(pSrv);
1048
1049 const uint16_t uPort = pSrv->uPort;
1050
1051 shClTransferHttpServerUnlock(pSrv);
1052
1053 return uPort;
1054}
1055
1056/**
1057 * Returns the number of registered HTTP server transfers of a HTTP server instance.
1058 *
1059 * @returns Number of registered transfers.
1060 * @param pSrv HTTP server instance to return registered transfers for.
1061 */
1062uint32_t ShClTransferHttpServerGetTransferCount(PSHCLHTTPSERVER pSrv)
1063{
1064 AssertPtrReturn(pSrv, 0);
1065
1066 shClTransferHttpServerLock(pSrv);
1067
1068 const uint32_t cTransfers = pSrv->cTransfers;
1069 LogFlowFunc(("cTransfers=%RU32\n", cTransfers));
1070
1071 shClTransferHttpServerUnlock(pSrv);
1072
1073 return cTransfers;
1074}
1075
1076/**
1077 * Returns an allocated string with a HTTP server instance's address.
1078 *
1079 * @returns Allocated string with a HTTP server instance's address, or NULL on OOM.
1080 * Needs to be free'd by the caller using RTStrFree().
1081 * @param pSrv HTTP server instance to return address for.
1082 */
1083char *ShClTransferHttpServerGetAddressA(PSHCLHTTPSERVER pSrv)
1084{
1085 AssertPtrReturn(pSrv, NULL);
1086
1087 shClTransferHttpServerLock(pSrv);
1088
1089 char *pszAddress = RTStrAPrintf2("%s:%RU16", shClTransferHttpServerGetHost(pSrv), pSrv->uPort);
1090 AssertPtr(pszAddress);
1091
1092 shClTransferHttpServerUnlock(pSrv);
1093
1094 return pszAddress;
1095}
1096
1097/**
1098 * Returns an allocated string with the URL of a given Shared Clipboard transfer ID.
1099 *
1100 * @returns Allocated string with the URL of a given Shared Clipboard transfer ID, or NULL if not found.
1101 * Needs to be free'd by the caller using RTStrFree().
1102 * @param pSrv HTTP server instance to return URL for.
1103 * @param idTransfer Transfer ID to return the URL for.
1104 */
1105char *ShClTransferHttpServerGetUrlA(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
1106{
1107 AssertPtrReturn(pSrv, NULL);
1108 AssertReturn(idTransfer != NIL_SHCLTRANSFERID, NULL);
1109
1110 shClTransferHttpServerLock(pSrv);
1111
1112 PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpServerGetTransferById(pSrv, idTransfer);
1113 if (!pSrvTx)
1114 {
1115 AssertFailed();
1116 shClTransferHttpServerUnlock(pSrv);
1117 return NULL;
1118 }
1119
1120 AssertReturn(RTStrNLen(pSrvTx->szPathVirtual, RTPATH_MAX), NULL);
1121 char *pszUrl = RTStrAPrintf2("%s:%RU16%s", shClTransferHttpServerGetHost(pSrv), pSrv->uPort, pSrvTx->szPathVirtual);
1122 AssertPtr(pszUrl);
1123
1124 shClTransferHttpServerUnlock(pSrv);
1125
1126 return pszUrl;
1127}
1128
1129/**
1130 * Returns whether a given HTTP server instance is initialized or not.
1131 *
1132 * @returns \c true if running, or \c false if not.
1133 * @param pSrv HTTP server instance to check initialized state for.
1134 */
1135bool ShClTransferHttpServerIsInitialized(PSHCLHTTPSERVER pSrv)
1136{
1137 AssertPtrReturn(pSrv, false);
1138
1139 return ASMAtomicReadBool(&pSrv->fInitialized);
1140}
1141
1142/**
1143 * Returns whether a given HTTP server instance is running or not.
1144 *
1145 * @returns \c true if running, or \c false if not.
1146 * @param pSrv HTTP server instance to check running state for.
1147 */
1148bool ShClTransferHttpServerIsRunning(PSHCLHTTPSERVER pSrv)
1149{
1150 AssertPtrReturn(pSrv, false);
1151
1152 return ASMAtomicReadBool(&pSrv->fRunning);
1153}
1154
1155/**
1156 * Waits for a status change.
1157 *
1158 * @returns VBox status code.
1159 * @retval VERR_STATE_CHANGED if the HTTP server was uninitialized.
1160 * @param pSrv HTTP server instance to wait for.
1161 * @param fStatus Status to wait for.
1162 * Multiple statuses are possible, @sa SHCLHTTPSERVERSTATUS.
1163 * @param msTimeout Timeout (in ms) to wait.
1164 */
1165int ShClTransferHttpServerWaitForStatusChange(PSHCLHTTPSERVER pSrv, SHCLHTTPSERVERSTATUS fStatus, RTMSINTERVAL msTimeout)
1166{
1167 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
1168 AssertMsgReturn(ASMAtomicReadBool(&pSrv->fInitialized), ("Server not initialized yet\n"), VERR_WRONG_ORDER);
1169
1170 shClTransferHttpServerLock(pSrv);
1171
1172 uint64_t const tsStartMs = RTTimeMilliTS();
1173
1174 int rc = VERR_TIMEOUT;
1175
1176 LogFlowFunc(("fStatus=%#x, msTimeout=%RU32 -- current is %#x\n", fStatus, msTimeout, pSrv->enmStatus));
1177
1178 while (RTTimeMilliTS() - tsStartMs <= msTimeout)
1179 {
1180 if (!pSrv->fInitialized)
1181 {
1182 rc = VERR_STATE_CHANGED;
1183 break;
1184 }
1185
1186 shClTransferHttpServerUnlock(pSrv); /* Leave lock before waiting. */
1187
1188 rc = RTSemEventWait(pSrv->StatusEvent, msTimeout);
1189
1190 shClTransferHttpServerLock(pSrv);
1191
1192 if (RT_FAILURE(rc))
1193 break;
1194
1195 LogFlowFunc(("Current status now is: %#x\n", pSrv->enmStatus));
1196 LogRel2(("Shared Clipboard: HTTP server entered status '%s'\n", shClTransferHttpServerStatusToStr(pSrv->enmStatus)));
1197
1198 if (pSrv->enmStatus & fStatus)
1199 {
1200 rc = VINF_SUCCESS;
1201 break;
1202 }
1203 }
1204
1205 shClTransferHttpServerUnlock(pSrv);
1206
1207 LogFlowFuncLeaveRC(rc);
1208 return rc;
1209}
1210
1211
1212/*********************************************************************************************************************************
1213* Public Shared Clipboard HTTP context functions *
1214*********************************************************************************************************************************/
1215
1216/**
1217 * Starts the HTTP server, if not started already.
1218 *
1219 * @returns VBox status code.
1220 * @param pCtx HTTP context to start HTTP server for.
1221 */
1222int ShClTransferHttpServerMaybeStart(PSHCLHTTPCONTEXT pCtx)
1223{
1224 int rc = VINF_SUCCESS;
1225
1226 LogFlowFuncEnter();
1227
1228 /* Start the built-in HTTP server to serve file(s). */
1229 if (!ShClTransferHttpServerIsRunning(&pCtx->HttpServer)) /* Only one HTTP server per transfer context. */
1230 rc = ShClTransferHttpServerStart(&pCtx->HttpServer, 32 /* cMaxAttempts */, NULL /* puPort */);
1231
1232 LogFlowFuncLeaveRC(rc);
1233 return rc;
1234}
1235
1236/**
1237 * Stops the HTTP server, if no running transfers are left.
1238 *
1239 * @returns VBox status code.
1240 * @param pCtx HTTP context to stop HTTP server for.
1241 */
1242int ShClTransferHttpServerMaybeStop(PSHCLHTTPCONTEXT pCtx)
1243{
1244 int rc = VINF_SUCCESS;
1245
1246 LogFlowFuncEnter();
1247
1248 if (ShClTransferHttpServerIsRunning(&pCtx->HttpServer))
1249 {
1250 /* No more registered transfers left? Tear down the HTTP server instance then. */
1251 if (ShClTransferHttpServerGetTransferCount(&pCtx->HttpServer) == 0)
1252 rc = ShClTransferHttpServerStop(&pCtx->HttpServer);
1253 }
1254
1255 LogFlowFuncLeaveRC(rc);
1256 return rc;
1257}
1258
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