VirtualBox

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

Last change on this file since 102797 was 102566, checked in by vboxsync, 12 months ago

Shared Clipboard/Transfers: Fixed memory leaks in shClTransferHttpQueryInfo() and shClTransferHttpServerDestroyInternal(). bugref:9437

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