VirtualBox

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

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

Shared Clipboard: Make sure to encode HTTP URLs, to also support files with special characters. bugref:9437

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