VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/networking/UINetworkReply.cpp

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VirtualBox/src/net/UINetworkManager.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VirtualBox/src/net/UINetworkManager.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VirtualBox/src/net/UINetworkManager.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VirtualBox/src/net/UINetworkManager.cpp74233
    /branches/VBox-4.2/src/VBox/Frontends/VirtualBox/src/net/UINetworkReply.cpp91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Frontends/VirtualBox/src/net/UINetworkReply.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Frontends/VirtualBox/src/net/UINetworkReply.cpp91223
    /branches/dsen/gui/src/VBox/Frontends/VirtualBox/src/net/UINetworkRequest.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VirtualBox/src/net/UINetworkRequest.cpp79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VirtualBox/src/net/UINetworkRequest.cpp79645-79692
File size: 33.8 KB
Line 
1/* $Id: UINetworkReply.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UINetworkReply stuff implementation.
4 */
5
6/*
7 * Copyright (C) 2012-2024 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/* Qt includes: */
29#include <QDir>
30#include <QFile>
31#include <QThread>
32#include <QVector>
33#include <QVariant>
34
35/* GUI includes: */
36#include "UILoggingDefs.h"
37#include "UINetworkReply.h"
38#include "UINetworkRequestManager.h"
39#include "UIExtraDataManager.h"
40#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
41# include "UIGlobalSession.h"
42# include "VBoxUtils.h"
43# include "CSystemProperties.h"
44#endif
45
46/* Other VBox includes: */
47#include <iprt/initterm.h>
48#include <iprt/crypto/pem.h>
49#include <iprt/crypto/store.h>
50#include <iprt/err.h>
51#include <iprt/http.h>
52#include <iprt/path.h>
53#include <iprt/sha.h>
54#include <iprt/string.h>
55#include <iprt/zip.h>
56
57
58/** QThread extension
59 * used as network-reply private thread interface. */
60class UINetworkReplyPrivateThread : public QThread
61{
62#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
63 Q_OBJECT;
64
65signals:
66
67 /** Notifies listeners about reply progress change.
68 * @param iBytesReceived Holds the current amount of bytes received.
69 * @param iBytesTotal Holds the total amount of bytes to be received. */
70 void sigDownloadProgress(qint64 iBytesReceived, qint64 iBytesTotal);
71#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
72
73public:
74
75 /** Constructs network-reply thread of the passed @a type for the passed @a url and @a requestHeaders. */
76 UINetworkReplyPrivateThread(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders);
77
78 /** @name APIs
79 * @{ */
80 /** Aborts reply. */
81 void abort();
82
83 /** Returns the URL of the reply which is the URL of the request for now. */
84 const QUrl& url() const { return m_url; }
85
86 /** Returns the last cached IPRT HTTP error of the reply. */
87 int error() const { return m_iError; }
88
89 /** Returns binary content of the reply. */
90 const QByteArray& readAll() const { return m_reply; }
91 /** Returns value for the cached reply header of the passed @a type. */
92 QString header(UINetworkReply::KnownHeader type) const;
93
94 /** Returns short descriptive context of thread's current operation. */
95 const QString context() const { return m_strContext; }
96 /** @} */
97
98private:
99
100 /** @name Helpers for HTTP and Certificates handling.
101 * @{ */
102 /** Applies configuration. */
103 int applyConfiguration();
104 /** Applies proxy rules. */
105 int applyProxyRules();
106 /** Applies security certificates. */
107 int applyHttpsCertificates();
108 /** Applies raw headers. */
109 int applyRawHeaders();
110 /** Performs main request. */
111 int performMainRequest();
112
113 /** Performs whole thread functionality. */
114 void run() RT_OVERRIDE RT_FINAL;
115
116 /** Handles download progress callback.
117 * @param cbDownloadTotal Brings the total amount of bytes to be received.
118 * @param cbDownloaded Brings the current amount of bytes received. */
119 void handleProgressChange(uint64_t cbDownloadTotal, uint64_t cbDownloaded);
120 /** @} */
121
122 /** @name Static helpers for HTTP and Certificates handling.
123 * @{ */
124 /** Returns full certificate file-name. */
125 static QString fullCertificateFileName();
126
127 /** Applies proxy rules.
128 * @remarks Implementation doesn't exists, to be removed? */
129 static int applyProxyRules(RTHTTP hHttp, const QString &strHostName, int iPort);
130
131 /** Applies raw headers.
132 * @param hHttp Brings the HTTP client instance.
133 * @param headers Brings the map of headers to be applied. */
134 static int applyRawHeaders(RTHTTP hHttp, const UserDictionary &headers);
135
136 /** Returns the number of certificates found in a search result array.
137 * @param pafFoundCerts Brings the array parallel to s_aCerts with the status of each wanted certificate. */
138 static unsigned countCertsFound(bool const *pafFoundCerts);
139
140 /** Returns whether we've found all the necessary certificates.
141 * @param pafFoundCerts Brings the array parallel to s_aCerts with the status of each wanted certificate. */
142 static bool areAllCertsFound(bool const *pafFoundCerts);
143
144 /** Refreshes the certificates.
145 * @param phStore On input, this holds the current store, so that we can fish out wanted
146 * certificates from it. On successful return, this is replaced with a new
147 * store reflecting the refrehsed content of @a pszCaCertFile.
148 * @param pafFoundCerts On input, this holds the certificates found in the current store.
149 * On return, this reflects what is current in the @a pszCaCertFile.
150 * The array runs parallel to s_aCerts.
151 * @param pszCaCertFile Where to write the refreshed certificates if we've managed to gather
152 * a collection that is at least as good as the old one. */
153 static int refreshCertificates(PRTCRSTORE phStore, bool *pafFoundCerts, const char *pszCaCertFile);
154
155 /** Redirects download progress callback to particular object which can handle it.
156 * @param hHttp Brings the HTTP client instance.
157 * @param pvUser Brings the convenience pointer for the
158 * user-agent object which should handle that callback.
159 * @param cbDownloadTotal Brings the total amount of bytes to be received.
160 * @param cbDownloaded Brings the current amount of bytes received. */
161 static DECLCALLBACK(void) handleProgressChange(RTHTTP hHttp, void *pvUser, uint64_t cbDownloadTotal, uint64_t cbDownloaded);
162 /** @} */
163
164 /** Holds the request type. */
165 const UINetworkRequestType m_type;
166 /** Holds the request url. */
167 const QUrl m_url;
168 /** Holds the request target. */
169 const QString m_strTarget;
170 /** Holds the request headers. */
171 const UserDictionary m_requestHeaders;
172
173 /** Holds the IPRT HTTP client instance handle. */
174 RTHTTP m_hHttp;
175 /** Holds the last cached IPRT HTTP error of the reply. */
176 int m_iError;
177 /** Holds short descriptive context of thread's current operation. */
178 QString m_strContext;
179 /** Holds the reply instance. */
180 QByteArray m_reply;
181 /** Holds the cached reply headers. */
182 UserDictionary m_headers;
183
184 /** Holds the details on the certificates we are after. */
185 static const RTCRCERTWANTED s_aCerts[];
186 /** Holds the certificate file name (no path). */
187 static const QString s_strCertificateFileName;
188
189#ifdef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
190public:
191 /** Starts the test routine. */
192 static void testIt(RTTEST hTest);
193#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
194};
195
196
197#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
198/** QObject extension
199 * used as network-reply private data interface. */
200class UINetworkReplyPrivate : public QObject
201{
202 Q_OBJECT;
203
204signals:
205
206 /** Notifies listeners about reply progress change.
207 * @param iBytesReceived Holds the current amount of bytes received.
208 * @param iBytesTotal Holds the total amount of bytes to be received. */
209 void downloadProgress(qint64 iBytesReceived, qint64 iBytesTotal);
210
211 /** Notifies listeners about reply has finished processing. */
212 void finished();
213
214public:
215
216 /** Constructs network-reply private data of the passed @a type for the passed @a url and @a requestHeaders. */
217 UINetworkReplyPrivate(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders);
218 /** Destructs reply private data. */
219 ~UINetworkReplyPrivate();
220
221 /** Aborts reply. */
222 void abort() { m_pThread->abort(); }
223
224 /** Returns URL of the reply. */
225 QUrl url() const { return m_pThread->url(); }
226
227 /** Returns the last cached error of the reply. */
228 UINetworkReply::NetworkError error() const { return m_error; }
229 /** Returns the user-oriented string corresponding to the last cached error of the reply. */
230 QString errorString() const;
231
232 /** Returns binary content of the reply. */
233 QByteArray readAll() const { return m_pThread->readAll(); }
234 /** Returns value for the cached reply header of the passed @a type. */
235 QString header(UINetworkReply::KnownHeader type) const { return m_pThread->header(type); }
236
237private slots:
238
239 /** Handles signal about reply has finished processing. */
240 void sltFinished();
241
242private:
243
244 /** Holds the last cached error of the reply. */
245 UINetworkReply::NetworkError m_error;
246
247 /** Holds the reply thread instance. */
248 UINetworkReplyPrivateThread *m_pThread;
249};
250#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
251
252
253/*********************************************************************************************************************************
254* Class UINetworkReplyPrivateThread implementation. *
255*********************************************************************************************************************************/
256
257/* static */
258const RTCRCERTWANTED UINetworkReplyPrivateThread::s_aCerts[] =
259{
260 /*[0] =*/
261 {
262 /*.pszSubject =*/
263 "C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA",
264 /*.cbEncoded =*/ 0x3b3,
265 /*.Sha1Fingerprint =*/ true,
266 /*.Sha512Fingerprint =*/ true,
267 /*.abSha1 =*/
268 {
269 0xa8, 0x98, 0x5d, 0x3a, 0x65, 0xe5, 0xe5, 0xc4, 0xb2, 0xd7,
270 0xd6, 0x6d, 0x40, 0xc6, 0xdd, 0x2f, 0xb1, 0x9c, 0x54, 0x36
271 },
272 /*.abSha512 =*/
273 {
274 0x53, 0xb4, 0x44, 0xe5, 0x65, 0x18, 0x32, 0x01,
275 0xa6, 0x1e, 0xeb, 0x46, 0x12, 0x09, 0xb2, 0xdc,
276 0x30, 0x89, 0x5e, 0xec, 0xa4, 0x87, 0x23, 0x8d,
277 0x15, 0xa0, 0x26, 0x73, 0x5f, 0x22, 0x9a, 0x81,
278 0x9e, 0x5b, 0x19, 0xcb, 0xd7, 0xe2, 0xfa, 0x27,
279 0x68, 0xab, 0x2a, 0x64, 0xf6, 0xeb, 0xcd, 0x9d,
280 0x1e, 0x72, 0x13, 0x41, 0xc9, 0xed, 0x5d, 0xd0,
281 0x9f, 0xc0, 0xd5, 0xe4, 0x3d, 0x68, 0xbc, 0xa7
282 },
283 /*.pvUser =*/ NULL,
284 },
285};
286
287/* static */
288const QString UINetworkReplyPrivateThread::s_strCertificateFileName = QString("vbox-ssl-cacertificate.crt");
289
290UINetworkReplyPrivateThread::UINetworkReplyPrivateThread(UINetworkRequestType type,
291 const QUrl &url,
292 const QString &strTarget,
293 const UserDictionary &requestHeaders)
294 : m_type(type)
295 , m_url(url)
296 , m_strTarget(strTarget)
297 , m_requestHeaders(requestHeaders)
298 , m_hHttp(NIL_RTHTTP)
299 , m_iError(VINF_SUCCESS)
300{
301}
302
303void UINetworkReplyPrivateThread::abort()
304{
305 /* Call for abort: */
306 if (m_hHttp != NIL_RTHTTP)
307 RTHttpAbort(m_hHttp);
308}
309
310QString UINetworkReplyPrivateThread::header(UINetworkReply::KnownHeader type) const
311{
312 /* Look for known header type: */
313 switch (type)
314 {
315 case UINetworkReply::ContentTypeHeader: return m_headers.value("Content-Type");
316 case UINetworkReply::ContentLengthHeader: return m_headers.value("Content-Length");
317 case UINetworkReply::LastModifiedHeader: return m_headers.value("Last-Modified");
318 case UINetworkReply::LocationHeader: return m_headers.value("Location");
319 default: break;
320 }
321 /* Return null-string by default: */
322 return QString();
323}
324
325int UINetworkReplyPrivateThread::applyConfiguration()
326{
327 /* Install downloading progress callback: */
328 return RTHttpSetDownloadProgressCallback(m_hHttp, &UINetworkReplyPrivateThread::handleProgressChange, this);
329}
330
331int UINetworkReplyPrivateThread::applyProxyRules()
332{
333 /* Set thread context: */
334 m_strContext = tr("During proxy configuration");
335
336#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
337 /* If the specific proxy settings are enabled, we'll use them
338 * unless user disabled that functionality manually. */
339 const CSystemProperties comProperties = gpGlobalSession->virtualBox().GetSystemProperties();
340 const KProxyMode enmProxyMode = comProperties.GetProxyMode();
341 AssertReturn(comProperties.isOk(), VERR_INTERNAL_ERROR_3);
342 switch (enmProxyMode)
343 {
344 case KProxyMode_Manual:
345 return RTHttpSetProxyByUrl(m_hHttp, comProperties.GetProxyURL().toUtf8().constData());
346 case KProxyMode_NoProxy:
347 return VINF_SUCCESS;
348 default:
349 break;
350 }
351#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
352
353 /* By default, use system proxy: */
354 return RTHttpUseSystemProxySettings(m_hHttp);
355}
356
357int UINetworkReplyPrivateThread::applyHttpsCertificates()
358{
359 /* Check if we really need SSL: */
360 if (!url().toString().startsWith("https:", Qt::CaseInsensitive))
361 return VINF_SUCCESS;
362
363 /* Set thread context: */
364 m_strContext = tr("During certificate downloading");
365
366 /*
367 * Calc the filename of the CA certificate file.
368 */
369 const QString strFullCertificateFileName(fullCertificateFileName());
370 QByteArray utf8FullCertificateFileName = strFullCertificateFileName.toUtf8();
371 const char *pszCaCertFile = utf8FullCertificateFileName.constData();
372
373 /*
374 * Check the state of our CA certificate file, it's one of the following:
375 * - Missing, recreate from scratch (= refresh).
376 * - Everything is there and it is less than 28 days old, do nothing.
377 * - Everything is there but it's older than 28 days, refresh.
378 * - Missing certificates and is older than 1 min, refresh.
379 *
380 * Start by creating a store for loading the current state into, as we'll
381 * be need that for the refresh.
382 */
383 RTCRSTORE hCurStore = NIL_RTCRSTORE;
384 int rc = RTCrStoreCreateInMem(&hCurStore, 256);
385 if (RT_SUCCESS(rc))
386 {
387 bool fRefresh = true;
388 bool afCertsFound[RT_ELEMENTS(s_aCerts)];
389 RT_ZERO(afCertsFound);
390
391 /*
392 * Load the file if it exists.
393 *
394 * To effect regular updates, we need the modification date of the file,
395 * so we use RTPathQueryInfoEx here and not RTFileExists.
396 */
397 RTFSOBJINFO Info;
398 rc = RTPathQueryInfoEx(pszCaCertFile, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
399 if ( RT_SUCCESS(rc)
400 && RTFS_IS_FILE(Info.Attr.fMode))
401 {
402 RTERRINFOSTATIC StaticErrInfo;
403 rc = RTCrStoreCertAddFromFile(hCurStore, RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR, pszCaCertFile,
404 RTErrInfoInitStatic(&StaticErrInfo));
405 if (RTErrInfoIsSet(&StaticErrInfo.Core))
406 LogRel(("checkCertificates: %s\n", StaticErrInfo.Core.pszMsg));
407 else
408 AssertRC(rc);
409
410 /*
411 * Scan the store the for certificates we need, then see what we
412 * need to do wrt file age.
413 */
414 rc = RTCrStoreCertCheckWanted(hCurStore, s_aCerts, RT_ELEMENTS(s_aCerts), afCertsFound);
415 AssertRC(rc);
416 RTTIMESPEC RefreshAge;
417 uint32_t cSecRefresh = rc == VINF_SUCCESS ? 28 * RT_SEC_1DAY /* all found */ : 60 /* stuff missing */;
418 fRefresh = RTTimeSpecCompare(&Info.ModificationTime, RTTimeSpecSubSeconds(RTTimeNow(&RefreshAge), cSecRefresh)) <= 0;
419 }
420
421 /*
422 * Refresh the file if necessary.
423 */
424 if (fRefresh)
425 refreshCertificates(&hCurStore, afCertsFound, pszCaCertFile);
426
427 RTCrStoreRelease(hCurStore);
428
429 /*
430 * Final verdict.
431 */
432 if (areAllCertsFound(afCertsFound))
433 rc = VINF_SUCCESS;
434 else
435 rc = VERR_NOT_FOUND; /** @todo r=bird: Why not try and let RTHttpGet* bitch if the necessary certs are missing? */
436
437 /*
438 * Set our custom CA file.
439 */
440 if (RT_SUCCESS(rc))
441 rc = RTHttpSetCAFile(m_hHttp, pszCaCertFile);
442 }
443 return rc;
444}
445
446int UINetworkReplyPrivateThread::applyRawHeaders()
447{
448 /* Set thread context: */
449 m_strContext = tr("During network request");
450
451 /* Make sure we have a raw headers at all: */
452 if (m_requestHeaders.isEmpty())
453 return VINF_SUCCESS;
454
455 /* Apply raw headers: */
456 return applyRawHeaders(m_hHttp, m_requestHeaders);
457}
458
459int UINetworkReplyPrivateThread::performMainRequest()
460{
461 /* Set thread context: */
462 m_strContext = tr("During network request");
463
464 /* Paranoia: */
465 m_reply.clear();
466
467 /* Prepare result: */
468 int rc = 0;
469
470 /* Depending on request type: */
471 switch (m_type)
472 {
473 case UINetworkRequestType_HEAD:
474 {
475 /* Perform blocking HTTP HEAD request: */
476 void *pvResponse = 0;
477 size_t cbResponse = 0;
478 rc = RTHttpGetHeaderBinary(m_hHttp, m_url.toString().toUtf8().constData(), &pvResponse, &cbResponse);
479 if (RT_SUCCESS(rc))
480 {
481 m_reply = QByteArray((char*)pvResponse, (int)cbResponse);
482 RTHttpFreeResponse(pvResponse);
483 }
484
485 /* Paranoia: */
486 m_headers.clear();
487
488 /* Parse header contents: */
489 const QString strHeaders = QString(m_reply);
490 foreach (const QString &strHeader, strHeaders.split("\n", Qt::SkipEmptyParts))
491 {
492 const QStringList values = strHeader.split(": ", Qt::SkipEmptyParts);
493 if (values.size() > 1)
494 m_headers[values.at(0)] = values.at(1);
495 }
496
497 /* Special handling of redirection header: */
498 if (rc == VERR_HTTP_REDIRECTED)
499 {
500 char *pszBuf = 0;
501 const int rrc = RTHttpGetRedirLocation(m_hHttp, &pszBuf);
502 if (RT_SUCCESS(rrc))
503 m_headers["Location"] = QString(pszBuf);
504 if (pszBuf)
505 RTMemFree(pszBuf);
506 }
507
508 break;
509 }
510 case UINetworkRequestType_GET:
511 {
512 /* Perform blocking HTTP GET request.
513 * Keep in mind that if the target parameter is provided,
514 * we are trying to download contents to file directly,
515 * otherwise it will be downloaded to memory and it's
516 * customer responsibility to save it afterwards. */
517 if (m_strTarget.isEmpty())
518 {
519 void *pvResponse = 0;
520 size_t cbResponse = 0;
521 rc = RTHttpGetBinary(m_hHttp, m_url.toString().toUtf8().constData(), &pvResponse, &cbResponse);
522 if (RT_SUCCESS(rc))
523 {
524 m_reply = QByteArray((char*)pvResponse, (int)cbResponse);
525 RTHttpFreeResponse(pvResponse);
526 }
527 }
528 else
529 {
530 rc = RTHttpGetFile(m_hHttp, m_url.toString().toUtf8().constData(), m_strTarget.toUtf8().constData());
531 if (RT_SUCCESS(rc))
532 {
533 QFile file(m_strTarget);
534 if (file.open(QIODevice::ReadOnly))
535 m_reply = file.readAll();
536 }
537 }
538
539 break;
540 }
541 default:
542 break;
543 }
544
545 /* Return result: */
546 return rc;
547}
548
549void UINetworkReplyPrivateThread::run()
550{
551 /* Create HTTP client: */
552 m_iError = RTHttpCreate(&m_hHttp);
553 if (RT_SUCCESS(m_iError))
554 {
555 /* Apply configuration: */
556 if (RT_SUCCESS(m_iError))
557 m_iError = applyConfiguration();
558
559 /* Apply proxy-rules: */
560 if (RT_SUCCESS(m_iError))
561 m_iError = applyProxyRules();
562
563 /* Apply https-certificates: */
564 if (RT_SUCCESS(m_iError))
565 m_iError = applyHttpsCertificates();
566
567 /* Assign raw-headers: */
568 if (RT_SUCCESS(m_iError))
569 m_iError = applyRawHeaders();
570
571 /* Perform main request: */
572 if (RT_SUCCESS(m_iError))
573 m_iError = performMainRequest();
574
575 /* Destroy HTTP client: */
576 RTHTTP hHttp = m_hHttp;
577 if (hHttp != NIL_RTHTTP)
578 {
579 /** @todo r=bird: There is a race here between this and abort()! */
580 m_hHttp = NIL_RTHTTP;
581 RTHttpDestroy(hHttp);
582 }
583 }
584}
585
586void UINetworkReplyPrivateThread::handleProgressChange(uint64_t cbDownloadTotal, uint64_t cbDownloaded)
587{
588#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
589 /* Notify listeners about progress change: */
590 emit sigDownloadProgress(cbDownloaded, cbDownloadTotal);
591#else /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
592 Q_UNUSED(cbDownloaded);
593 Q_UNUSED(cbDownloadTotal);
594#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
595}
596
597/* static */
598QString UINetworkReplyPrivateThread::fullCertificateFileName()
599{
600#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
601 const QDir homeDir(QDir::toNativeSeparators(gpGlobalSession->homeFolder()));
602 return QDir::toNativeSeparators(homeDir.absoluteFilePath(s_strCertificateFileName));
603#else /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
604 return QString("/not/such/agency/non-existing-file.cer");
605#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
606}
607
608/* static */
609int UINetworkReplyPrivateThread::applyRawHeaders(RTHTTP hHttp, const UserDictionary &headers)
610{
611 /* Make sure HTTP is created: */
612 if (hHttp == NIL_RTHTTP)
613 return VERR_INVALID_HANDLE;
614
615 /* We should format them first: */
616 QVector<QByteArray> formattedHeaders;
617 QVector<const char*> formattedHeaderPointers;
618 foreach (const QString &header, headers.keys())
619 {
620 /* Prepare formatted representation: */
621 QString strFormattedString = QString("%1: %2").arg(header, headers.value(header));
622 formattedHeaders << strFormattedString.toUtf8();
623 formattedHeaderPointers << formattedHeaders.last().constData();
624 }
625 const char **ppFormattedHeaders = formattedHeaderPointers.data();
626
627 /* Apply HTTP headers: */
628 return RTHttpSetHeaders(hHttp, formattedHeaderPointers.size(), ppFormattedHeaders);
629}
630
631/* static */
632unsigned UINetworkReplyPrivateThread::countCertsFound(bool const *pafFoundCerts)
633{
634 unsigned cFound = 0;
635 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCerts); i++)
636 cFound += pafFoundCerts[i];
637 return cFound;
638}
639
640/* static */
641bool UINetworkReplyPrivateThread::areAllCertsFound(bool const *pafFoundCerts)
642{
643 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCerts); i++)
644 if (!pafFoundCerts[i])
645 return false;
646 return true;
647}
648
649/* static */
650int UINetworkReplyPrivateThread::refreshCertificates(PRTCRSTORE phStore, bool *pafFoundCerts, const char *pszCaCertFile)
651{
652 /*
653 * Collect the standard assortment of SSL certificates.
654 */
655 uint32_t cHint = RTCrStoreCertCount(*phStore);
656 RTCRSTORE hNewStore;
657 int rc = RTCrStoreCreateInMem(&hNewStore, cHint > 32 && cHint < _32K ? cHint + 16 : 256);
658 if (RT_SUCCESS(rc))
659 {
660 RTERRINFOSTATIC StaticErrInfo;
661 rc = RTHttpGatherCaCertsInStore(hNewStore, 0 /*fFlags*/, RTErrInfoInitStatic(&StaticErrInfo));
662 if (RTErrInfoIsSet(&StaticErrInfo.Core))
663 LogRel(("refreshCertificates/#1: %s\n", StaticErrInfo.Core.pszMsg));
664 else if (rc == VERR_NOT_FOUND)
665 LogRel(("refreshCertificates/#1: No trusted SSL certs found on the system, will try download...\n"));
666 else
667 AssertLogRelRC(rc);
668 if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
669 {
670 /*
671 * Check and see what we've got. If we haven't got all we desire,
672 * try add it from the previous store.
673 */
674 bool afNewFoundCerts[RT_ELEMENTS(s_aCerts)];
675 RT_ZERO(afNewFoundCerts); /* paranoia */
676
677 rc = RTCrStoreCertCheckWanted(hNewStore, s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts);
678 AssertLogRelRC(rc);
679 Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
680 if (rc != VINF_SUCCESS)
681 {
682 rc = RTCrStoreCertAddWantedFromStore(hNewStore,
683 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
684 *phStore, s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts);
685 AssertLogRelRC(rc);
686 Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
687 }
688
689 /*
690 * If that didn't help, seek out certificates in more obscure places,
691 * like java, mozilla and mutt.
692 */
693 if (rc != VINF_SUCCESS)
694 {
695 rc = RTCrStoreCertAddWantedFromFishingExpedition(hNewStore,
696 RTCRCERTCTX_F_ADD_IF_NOT_FOUND
697 | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
698 s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts,
699 RTErrInfoInitStatic(&StaticErrInfo));
700 if (RTErrInfoIsSet(&StaticErrInfo.Core))
701 LogRel(("refreshCertificates/#2: %s\n", StaticErrInfo.Core.pszMsg));
702 Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
703 }
704
705 /*
706 * If we've got the same or better hit rate than the old store,
707 * replace the CA certs file.
708 */
709 if ( areAllCertsFound(afNewFoundCerts)
710 || countCertsFound(afNewFoundCerts) >= countCertsFound(pafFoundCerts) )
711 {
712 rc = RTCrStoreCertExportAsPem(hNewStore, 0 /*fFlags*/, pszCaCertFile);
713 if (RT_SUCCESS(rc))
714 {
715 LogRel(("refreshCertificates/#3: Found %u/%u SSL certs we/you trust (previously %u/%u).\n",
716 countCertsFound(afNewFoundCerts), RTCrStoreCertCount(hNewStore),
717 countCertsFound(pafFoundCerts), RTCrStoreCertCount(*phStore) ));
718
719 memcpy(pafFoundCerts, afNewFoundCerts, sizeof(afNewFoundCerts));
720 RTCrStoreRelease(*phStore);
721 *phStore = hNewStore;
722 hNewStore = NIL_RTCRSTORE;
723 }
724 else
725 {
726 RT_ZERO(pafFoundCerts);
727 LogRel(("refreshCertificates/#3: RTCrStoreCertExportAsPem unexpectedly failed with %Rrc\n", rc));
728 }
729 }
730 else
731 LogRel(("refreshCertificates/#3: Sticking with the old file, missing essential certs.\n"));
732 }
733 RTCrStoreRelease(hNewStore);
734 }
735 return rc;
736}
737
738/* static */
739DECLCALLBACK(void) UINetworkReplyPrivateThread::handleProgressChange(RTHTTP hHttp, void *pvUser, uint64_t cbDownloadTotal, uint64_t cbDownloaded)
740{
741 /* Redirect callback to particular object: */
742 Q_UNUSED(hHttp);
743 AssertPtrReturnVoid(pvUser);
744 static_cast<UINetworkReplyPrivateThread*>(pvUser)->handleProgressChange(cbDownloadTotal, cbDownloaded);
745}
746
747
748#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
749
750
751/*********************************************************************************************************************************
752* Class UINetworkReplyPrivate implementation. *
753*********************************************************************************************************************************/
754
755UINetworkReplyPrivate::UINetworkReplyPrivate(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders)
756 : m_error(UINetworkReply::NoError)
757 , m_pThread(0)
758{
759 /* Create and run reply thread: */
760 m_pThread = new UINetworkReplyPrivateThread(type, url, strTarget, requestHeaders);
761 connect(m_pThread, &UINetworkReplyPrivateThread::sigDownloadProgress,
762 this, &UINetworkReplyPrivate::downloadProgress, Qt::QueuedConnection);
763 connect(m_pThread, &UINetworkReplyPrivateThread::finished,
764 this, &UINetworkReplyPrivate::sltFinished);
765 m_pThread->start();
766}
767
768UINetworkReplyPrivate::~UINetworkReplyPrivate()
769{
770 /* Terminate network-reply thread: */
771 m_pThread->abort();
772 m_pThread->wait();
773 delete m_pThread;
774 m_pThread = 0;
775}
776
777QString UINetworkReplyPrivate::errorString() const
778{
779 /* Look for known error codes: */
780 switch (m_error)
781 {
782 case UINetworkReply::NoError: break;
783 case UINetworkReply::RemoteHostClosedError: return QString("%1: %2").arg(m_pThread->context(), tr("Unable to initialize HTTP library"));
784 case UINetworkReply::UrlNotFoundError: return QString("%1: %2").arg(m_pThread->context(), tr("Url not found on the server"));
785 case UINetworkReply::HostNotFoundError: return QString("%1: %2").arg(m_pThread->context(), tr("Host not found"));
786 case UINetworkReply::ContentAccessDenied: return QString("%1: %2").arg(m_pThread->context(), tr("Content access denied"));
787 case UINetworkReply::ProtocolFailure: return QString("%1: %2").arg(m_pThread->context(), tr("Protocol failure"));
788 case UINetworkReply::ConnectionRefusedError: return QString("%1: %2").arg(m_pThread->context(), tr("Connection refused"));
789 case UINetworkReply::SslHandshakeFailedError: return QString("%1: %2").arg(m_pThread->context(), tr("SSL authentication failed"));
790 case UINetworkReply::AuthenticationRequiredError: return QString("%1: %2").arg(m_pThread->context(), tr("Wrong SSL certificate format"));
791 case UINetworkReply::ContentReSendError: return QString("%1: %2").arg(m_pThread->context(), tr("Content moved"));
792 case UINetworkReply::ProxyNotFoundError: return QString("%1: %2").arg(m_pThread->context(), tr("Proxy not found"));
793 default: return QString("%1: %2").arg(m_pThread->context(), tr("Unknown reason"));
794 }
795 /* Return null-string by default: */
796 return QString();
797}
798
799void UINetworkReplyPrivate::sltFinished()
800{
801 /* Look for known error codes: */
802 switch (m_pThread->error())
803 {
804 case VINF_SUCCESS: m_error = UINetworkReply::NoError; break;
805 case VERR_HTTP_INIT_FAILED: m_error = UINetworkReply::RemoteHostClosedError; break;
806 case VERR_HTTP_NOT_FOUND: m_error = UINetworkReply::UrlNotFoundError; break;
807 case VERR_HTTP_HOST_NOT_FOUND: m_error = UINetworkReply::HostNotFoundError; break;
808 case VERR_HTTP_ACCESS_DENIED: m_error = UINetworkReply::ContentAccessDenied; break;
809 case VERR_HTTP_BAD_REQUEST: m_error = UINetworkReply::ProtocolFailure; break;
810 case VERR_HTTP_COULDNT_CONNECT: m_error = UINetworkReply::ConnectionRefusedError; break;
811 case VERR_HTTP_SSL_CONNECT_ERROR: m_error = UINetworkReply::SslHandshakeFailedError; break;
812 case VERR_HTTP_CACERT_WRONG_FORMAT: m_error = UINetworkReply::AuthenticationRequiredError; break;
813 case VERR_HTTP_CACERT_CANNOT_AUTHENTICATE: m_error = UINetworkReply::AuthenticationRequiredError; break;
814 case VERR_HTTP_ABORTED: m_error = UINetworkReply::OperationCanceledError; break;
815 case VERR_HTTP_REDIRECTED: m_error = UINetworkReply::ContentReSendError; break;
816 case VERR_HTTP_PROXY_NOT_FOUND: m_error = UINetworkReply::ProxyNotFoundError; break;
817 default: m_error = UINetworkReply::UnknownNetworkError; break;
818 }
819 /* Redirect signal to external listeners: */
820 emit finished();
821}
822
823
824/*********************************************************************************************************************************
825* Class UINetworkReply implementation. *
826*********************************************************************************************************************************/
827
828UINetworkReply::UINetworkReply(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders)
829 : m_pReply(new UINetworkReplyPrivate(type, url, strTarget, requestHeaders))
830{
831 /* Prepare network-reply object connections: */
832 connect(m_pReply, &UINetworkReplyPrivate::downloadProgress, this, &UINetworkReply::downloadProgress);
833 connect(m_pReply, &UINetworkReplyPrivate::finished, this, &UINetworkReply::finished);
834}
835
836UINetworkReply::~UINetworkReply()
837{
838 /* Cleanup network-reply object: */
839 if (m_pReply)
840 {
841 delete m_pReply;
842 m_pReply = 0;
843 }
844}
845
846void UINetworkReply::abort()
847{
848 return m_pReply->abort();
849}
850
851QUrl UINetworkReply::url() const
852{
853 return m_pReply->url();
854}
855
856UINetworkReply::NetworkError UINetworkReply::error() const
857{
858 return m_pReply->error();
859}
860
861QString UINetworkReply::errorString() const
862{
863 return m_pReply->errorString();
864}
865
866QByteArray UINetworkReply::readAll() const
867{
868 return m_pReply->readAll();
869}
870
871QVariant UINetworkReply::header(UINetworkReply::KnownHeader header) const
872{
873 return m_pReply->header(header);
874}
875
876#include "UINetworkReply.moc"
877
878#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
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