VirtualBox

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

Last change on this file since 103131 was 98103, checked in by vboxsync, 2 years 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: 34.4 KB
Line 
1/* $Id: UINetworkReply.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UINetworkReply stuff implementation.
4 */
5
6/*
7 * Copyright (C) 2012-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/* Qt includes: */
29#include <QDir>
30#include <QFile>
31#include <QThread>
32#include <QVector>
33#include <QVariant>
34
35/* GUI includes: */
36#include "UINetworkReply.h"
37#include "UINetworkRequestManager.h"
38#include "UIExtraDataManager.h"
39#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
40# include "UICommon.h"
41# include "VBoxUtils.h"
42# include "CSystemProperties.h"
43#endif
44
45/* Other VBox includes: */
46#include <iprt/initterm.h>
47#include <iprt/crypto/pem.h>
48#include <iprt/crypto/store.h>
49#include <iprt/err.h>
50#include <iprt/http.h>
51#include <iprt/path.h>
52#include <iprt/sha.h>
53#include <iprt/string.h>
54#include <iprt/zip.h>
55#include <VBox/log.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();
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 full error template in "Context description: Error description" form. */
245 QString m_strErrorTemplate;
246
247 /** Holds the last cached error of the reply. */
248 UINetworkReply::NetworkError m_error;
249
250 /** Holds the reply thread instance. */
251 UINetworkReplyPrivateThread *m_pThread;
252};
253#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
254
255
256/*********************************************************************************************************************************
257* Class UINetworkReplyPrivateThread implementation. *
258*********************************************************************************************************************************/
259
260/* static */
261const RTCRCERTWANTED UINetworkReplyPrivateThread::s_aCerts[] =
262{
263 /*[0] =*/
264 {
265 /*.pszSubject =*/
266 "C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, "
267 "CN=VeriSign Class 3 Public Primary Certification Authority - G5",
268 /*.cbEncoded =*/ 0x4d7,
269 /*.Sha1Fingerprint =*/ true,
270 /*.Sha512Fingerprint =*/ true,
271 /*.abSha1 =*/
272 {
273 0x4e, 0xb6, 0xd5, 0x78, 0x49, 0x9b, 0x1c, 0xcf, 0x5f, 0x58,
274 0x1e, 0xad, 0x56, 0xbe, 0x3d, 0x9b, 0x67, 0x44, 0xa5, 0xe5
275 },
276 /*.abSha512 =*/
277 {
278 0xd4, 0xf8, 0x10, 0x54, 0x72, 0x77, 0x0a, 0x2d,
279 0xe3, 0x17, 0xb3, 0xcf, 0xed, 0x61, 0xae, 0x5c,
280 0x5d, 0x3e, 0xde, 0xa1, 0x41, 0x35, 0xb2, 0xdf,
281 0x60, 0xe2, 0x61, 0xfe, 0x3a, 0xc1, 0x66, 0xa3,
282 0x3c, 0x88, 0x54, 0x04, 0x4f, 0x1d, 0x13, 0x46,
283 0xe3, 0x8c, 0x06, 0x92, 0x9d, 0x70, 0x54, 0xc3,
284 0x44, 0xeb, 0x2c, 0x74, 0x25, 0x9e, 0x5d, 0xfb,
285 0xd2, 0x6b, 0xa8, 0x9a, 0xf0, 0xb3, 0x6a, 0x01
286 },
287 /*.pvUser =*/ NULL,
288 },
289};
290
291/* static */
292const QString UINetworkReplyPrivateThread::s_strCertificateFileName = QString("vbox-ssl-cacertificate.crt");
293
294UINetworkReplyPrivateThread::UINetworkReplyPrivateThread(UINetworkRequestType type,
295 const QUrl &url,
296 const QString &strTarget,
297 const UserDictionary &requestHeaders)
298 : m_type(type)
299 , m_url(url)
300 , m_strTarget(strTarget)
301 , m_requestHeaders(requestHeaders)
302 , m_hHttp(NIL_RTHTTP)
303 , m_iError(VINF_SUCCESS)
304{
305}
306
307void UINetworkReplyPrivateThread::abort()
308{
309 /* Call for abort: */
310 if (m_hHttp != NIL_RTHTTP)
311 RTHttpAbort(m_hHttp);
312}
313
314QString UINetworkReplyPrivateThread::header(UINetworkReply::KnownHeader type) const
315{
316 /* Look for known header type: */
317 switch (type)
318 {
319 case UINetworkReply::ContentTypeHeader: return m_headers.value("Content-Type");
320 case UINetworkReply::ContentLengthHeader: return m_headers.value("Content-Length");
321 case UINetworkReply::LastModifiedHeader: return m_headers.value("Last-Modified");
322 case UINetworkReply::LocationHeader: return m_headers.value("Location");
323 default: break;
324 }
325 /* Return null-string by default: */
326 return QString();
327}
328
329int UINetworkReplyPrivateThread::applyConfiguration()
330{
331 /* Install downloading progress callback: */
332 return RTHttpSetDownloadProgressCallback(m_hHttp, &UINetworkReplyPrivateThread::handleProgressChange, this);
333}
334
335int UINetworkReplyPrivateThread::applyProxyRules()
336{
337 /* Set thread context: */
338 m_strContext = tr("During proxy configuration");
339
340#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
341 /* If the specific proxy settings are enabled, we'll use them
342 * unless user disabled that functionality manually. */
343 const CSystemProperties comProperties = uiCommon().virtualBox().GetSystemProperties();
344 const KProxyMode enmProxyMode = comProperties.GetProxyMode();
345 AssertReturn(comProperties.isOk(), VERR_INTERNAL_ERROR_3);
346 switch (enmProxyMode)
347 {
348 case KProxyMode_Manual:
349 return RTHttpSetProxyByUrl(m_hHttp, comProperties.GetProxyURL().toUtf8().constData());
350 case KProxyMode_NoProxy:
351 return VINF_SUCCESS;
352 default:
353 break;
354 }
355#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
356
357 /* By default, use system proxy: */
358 return RTHttpUseSystemProxySettings(m_hHttp);
359}
360
361int UINetworkReplyPrivateThread::applyHttpsCertificates()
362{
363 /* Check if we really need SSL: */
364 if (!url().toString().startsWith("https:", Qt::CaseInsensitive))
365 return VINF_SUCCESS;
366
367 /* Set thread context: */
368 m_strContext = tr("During certificate downloading");
369
370 /*
371 * Calc the filename of the CA certificate file.
372 */
373 const QString strFullCertificateFileName(fullCertificateFileName());
374 QByteArray utf8FullCertificateFileName = strFullCertificateFileName.toUtf8();
375 const char *pszCaCertFile = utf8FullCertificateFileName.constData();
376
377 /*
378 * Check the state of our CA certificate file, it's one of the following:
379 * - Missing, recreate from scratch (= refresh).
380 * - Everything is there and it is less than 28 days old, do nothing.
381 * - Everything is there but it's older than 28 days, refresh.
382 * - Missing certificates and is older than 1 min, refresh.
383 *
384 * Start by creating a store for loading the current state into, as we'll
385 * be need that for the refresh.
386 */
387 RTCRSTORE hCurStore = NIL_RTCRSTORE;
388 int rc = RTCrStoreCreateInMem(&hCurStore, 256);
389 if (RT_SUCCESS(rc))
390 {
391 bool fRefresh = true;
392 bool afCertsFound[RT_ELEMENTS(s_aCerts)];
393 RT_ZERO(afCertsFound);
394
395 /*
396 * Load the file if it exists.
397 *
398 * To effect regular updates, we need the modification date of the file,
399 * so we use RTPathQueryInfoEx here and not RTFileExists.
400 */
401 RTFSOBJINFO Info;
402 rc = RTPathQueryInfoEx(pszCaCertFile, &Info, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
403 if ( RT_SUCCESS(rc)
404 && RTFS_IS_FILE(Info.Attr.fMode))
405 {
406 RTERRINFOSTATIC StaticErrInfo;
407 rc = RTCrStoreCertAddFromFile(hCurStore, RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR, pszCaCertFile,
408 RTErrInfoInitStatic(&StaticErrInfo));
409 if (RTErrInfoIsSet(&StaticErrInfo.Core))
410 LogRel(("checkCertificates: %s\n", StaticErrInfo.Core.pszMsg));
411 else
412 AssertRC(rc);
413
414 /*
415 * Scan the store the for certificates we need, then see what we
416 * need to do wrt file age.
417 */
418 rc = RTCrStoreCertCheckWanted(hCurStore, s_aCerts, RT_ELEMENTS(s_aCerts), afCertsFound);
419 AssertRC(rc);
420 RTTIMESPEC RefreshAge;
421 uint32_t cSecRefresh = rc == VINF_SUCCESS ? 28 * RT_SEC_1DAY /* all found */ : 60 /* stuff missing */;
422 fRefresh = RTTimeSpecCompare(&Info.ModificationTime, RTTimeSpecSubSeconds(RTTimeNow(&RefreshAge), cSecRefresh)) <= 0;
423 }
424
425 /*
426 * Refresh the file if necessary.
427 */
428 if (fRefresh)
429 refreshCertificates(&hCurStore, afCertsFound, pszCaCertFile);
430
431 RTCrStoreRelease(hCurStore);
432
433 /*
434 * Final verdict.
435 */
436 if (areAllCertsFound(afCertsFound))
437 rc = VINF_SUCCESS;
438 else
439 rc = VERR_NOT_FOUND; /** @todo r=bird: Why not try and let RTHttpGet* bitch if the necessary certs are missing? */
440
441 /*
442 * Set our custom CA file.
443 */
444 if (RT_SUCCESS(rc))
445 rc = RTHttpSetCAFile(m_hHttp, pszCaCertFile);
446 }
447 return rc;
448}
449
450int UINetworkReplyPrivateThread::applyRawHeaders()
451{
452 /* Set thread context: */
453 m_strContext = tr("During network request");
454
455 /* Make sure we have a raw headers at all: */
456 if (m_requestHeaders.isEmpty())
457 return VINF_SUCCESS;
458
459 /* Apply raw headers: */
460 return applyRawHeaders(m_hHttp, m_requestHeaders);
461}
462
463int UINetworkReplyPrivateThread::performMainRequest()
464{
465 /* Set thread context: */
466 m_strContext = tr("During network request");
467
468 /* Paranoia: */
469 m_reply.clear();
470
471 /* Prepare result: */
472 int rc = 0;
473
474 /* Depending on request type: */
475 switch (m_type)
476 {
477 case UINetworkRequestType_HEAD:
478 {
479 /* Perform blocking HTTP HEAD request: */
480 void *pvResponse = 0;
481 size_t cbResponse = 0;
482 rc = RTHttpGetHeaderBinary(m_hHttp, m_url.toString().toUtf8().constData(), &pvResponse, &cbResponse);
483 if (RT_SUCCESS(rc))
484 {
485 m_reply = QByteArray((char*)pvResponse, (int)cbResponse);
486 RTHttpFreeResponse(pvResponse);
487 }
488
489 /* Paranoia: */
490 m_headers.clear();
491
492 /* Parse header contents: */
493 const QString strHeaders = QString(m_reply);
494#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
495 const QStringList headers = strHeaders.split("\n", Qt::SkipEmptyParts);
496#else
497 const QStringList headers = strHeaders.split("\n", QString::SkipEmptyParts);
498#endif
499 foreach (const QString &strHeader, headers)
500 {
501#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
502 const QStringList values = strHeader.split(": ", Qt::SkipEmptyParts);
503#else
504 const QStringList values = strHeader.split(": ", QString::SkipEmptyParts);
505#endif
506 if (values.size() > 1)
507 m_headers[values.at(0)] = values.at(1);
508 }
509
510 /* Special handling of redirection header: */
511 if (rc == VERR_HTTP_REDIRECTED)
512 {
513 char *pszBuf = 0;
514 const int rrc = RTHttpGetRedirLocation(m_hHttp, &pszBuf);
515 if (RT_SUCCESS(rrc))
516 m_headers["Location"] = QString(pszBuf);
517 if (pszBuf)
518 RTMemFree(pszBuf);
519 }
520
521 break;
522 }
523 case UINetworkRequestType_GET:
524 {
525 /* Perform blocking HTTP GET request.
526 * Keep in mind that if the target parameter is provided,
527 * we are trying to download contents to file directly,
528 * otherwise it will be downloaded to memory and it's
529 * customer responsibility to save it afterwards. */
530 if (m_strTarget.isEmpty())
531 {
532 void *pvResponse = 0;
533 size_t cbResponse = 0;
534 rc = RTHttpGetBinary(m_hHttp, m_url.toString().toUtf8().constData(), &pvResponse, &cbResponse);
535 if (RT_SUCCESS(rc))
536 {
537 m_reply = QByteArray((char*)pvResponse, (int)cbResponse);
538 RTHttpFreeResponse(pvResponse);
539 }
540 }
541 else
542 {
543 rc = RTHttpGetFile(m_hHttp, m_url.toString().toUtf8().constData(), m_strTarget.toUtf8().constData());
544 if (RT_SUCCESS(rc))
545 {
546 QFile file(m_strTarget);
547 if (file.open(QIODevice::ReadOnly))
548 m_reply = file.readAll();
549 }
550 }
551
552 break;
553 }
554 default:
555 break;
556 }
557
558 /* Return result: */
559 return rc;
560}
561
562void UINetworkReplyPrivateThread::run()
563{
564 /* Create HTTP client: */
565 m_iError = RTHttpCreate(&m_hHttp);
566 if (RT_SUCCESS(m_iError))
567 {
568 /* Apply configuration: */
569 if (RT_SUCCESS(m_iError))
570 m_iError = applyConfiguration();
571
572 /* Apply proxy-rules: */
573 if (RT_SUCCESS(m_iError))
574 m_iError = applyProxyRules();
575
576 /* Apply https-certificates: */
577 if (RT_SUCCESS(m_iError))
578 m_iError = applyHttpsCertificates();
579
580 /* Assign raw-headers: */
581 if (RT_SUCCESS(m_iError))
582 m_iError = applyRawHeaders();
583
584 /* Perform main request: */
585 if (RT_SUCCESS(m_iError))
586 m_iError = performMainRequest();
587
588 /* Destroy HTTP client: */
589 RTHTTP hHttp = m_hHttp;
590 if (hHttp != NIL_RTHTTP)
591 {
592 /** @todo r=bird: There is a race here between this and abort()! */
593 m_hHttp = NIL_RTHTTP;
594 RTHttpDestroy(hHttp);
595 }
596 }
597}
598
599void UINetworkReplyPrivateThread::handleProgressChange(uint64_t cbDownloadTotal, uint64_t cbDownloaded)
600{
601#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
602 /* Notify listeners about progress change: */
603 emit sigDownloadProgress(cbDownloaded, cbDownloadTotal);
604#else /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
605 Q_UNUSED(cbDownloaded);
606 Q_UNUSED(cbDownloadTotal);
607#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
608}
609
610/* static */
611QString UINetworkReplyPrivateThread::fullCertificateFileName()
612{
613#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
614 const QDir homeDir(QDir::toNativeSeparators(uiCommon().homeFolder()));
615 return QDir::toNativeSeparators(homeDir.absoluteFilePath(s_strCertificateFileName));
616#else /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
617 return QString("/not/such/agency/non-existing-file.cer");
618#endif /* VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
619}
620
621/* static */
622int UINetworkReplyPrivateThread::applyRawHeaders(RTHTTP hHttp, const UserDictionary &headers)
623{
624 /* Make sure HTTP is created: */
625 if (hHttp == NIL_RTHTTP)
626 return VERR_INVALID_HANDLE;
627
628 /* We should format them first: */
629 QVector<QByteArray> formattedHeaders;
630 QVector<const char*> formattedHeaderPointers;
631 foreach (const QString &header, headers.keys())
632 {
633 /* Prepare formatted representation: */
634 QString strFormattedString = QString("%1: %2").arg(header, headers.value(header));
635 formattedHeaders << strFormattedString.toUtf8();
636 formattedHeaderPointers << formattedHeaders.last().constData();
637 }
638 const char **ppFormattedHeaders = formattedHeaderPointers.data();
639
640 /* Apply HTTP headers: */
641 return RTHttpSetHeaders(hHttp, formattedHeaderPointers.size(), ppFormattedHeaders);
642}
643
644/* static */
645unsigned UINetworkReplyPrivateThread::countCertsFound(bool const *pafFoundCerts)
646{
647 unsigned cFound = 0;
648 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCerts); i++)
649 cFound += pafFoundCerts[i];
650 return cFound;
651}
652
653/* static */
654bool UINetworkReplyPrivateThread::areAllCertsFound(bool const *pafFoundCerts)
655{
656 for (uint32_t i = 0; i < RT_ELEMENTS(s_aCerts); i++)
657 if (!pafFoundCerts[i])
658 return false;
659 return true;
660}
661
662/* static */
663int UINetworkReplyPrivateThread::refreshCertificates(PRTCRSTORE phStore, bool *pafFoundCerts, const char *pszCaCertFile)
664{
665 /*
666 * Collect the standard assortment of SSL certificates.
667 */
668 uint32_t cHint = RTCrStoreCertCount(*phStore);
669 RTCRSTORE hNewStore;
670 int rc = RTCrStoreCreateInMem(&hNewStore, cHint > 32 && cHint < _32K ? cHint + 16 : 256);
671 if (RT_SUCCESS(rc))
672 {
673 RTERRINFOSTATIC StaticErrInfo;
674 rc = RTHttpGatherCaCertsInStore(hNewStore, 0 /*fFlags*/, RTErrInfoInitStatic(&StaticErrInfo));
675 if (RTErrInfoIsSet(&StaticErrInfo.Core))
676 LogRel(("refreshCertificates/#1: %s\n", StaticErrInfo.Core.pszMsg));
677 else if (rc == VERR_NOT_FOUND)
678 LogRel(("refreshCertificates/#1: No trusted SSL certs found on the system, will try download...\n"));
679 else
680 AssertLogRelRC(rc);
681 if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
682 {
683 /*
684 * Check and see what we've got. If we haven't got all we desire,
685 * try add it from the previous store.
686 */
687 bool afNewFoundCerts[RT_ELEMENTS(s_aCerts)];
688 RT_ZERO(afNewFoundCerts); /* paranoia */
689
690 rc = RTCrStoreCertCheckWanted(hNewStore, s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts);
691 AssertLogRelRC(rc);
692 Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
693 if (rc != VINF_SUCCESS)
694 {
695 rc = RTCrStoreCertAddWantedFromStore(hNewStore,
696 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
697 *phStore, s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts);
698 AssertLogRelRC(rc);
699 Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
700 }
701
702 /*
703 * If that didn't help, seek out certificates in more obscure places,
704 * like java, mozilla and mutt.
705 */
706 if (rc != VINF_SUCCESS)
707 {
708 rc = RTCrStoreCertAddWantedFromFishingExpedition(hNewStore,
709 RTCRCERTCTX_F_ADD_IF_NOT_FOUND
710 | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
711 s_aCerts, RT_ELEMENTS(s_aCerts), afNewFoundCerts,
712 RTErrInfoInitStatic(&StaticErrInfo));
713 if (RTErrInfoIsSet(&StaticErrInfo.Core))
714 LogRel(("refreshCertificates/#2: %s\n", StaticErrInfo.Core.pszMsg));
715 Assert(rc != VINF_SUCCESS || areAllCertsFound(afNewFoundCerts));
716 }
717
718 /*
719 * If we've got the same or better hit rate than the old store,
720 * replace the CA certs file.
721 */
722 if ( areAllCertsFound(afNewFoundCerts)
723 || countCertsFound(afNewFoundCerts) >= countCertsFound(pafFoundCerts) )
724 {
725 rc = RTCrStoreCertExportAsPem(hNewStore, 0 /*fFlags*/, pszCaCertFile);
726 if (RT_SUCCESS(rc))
727 {
728 LogRel(("refreshCertificates/#3: Found %u/%u SSL certs we/you trust (previously %u/%u).\n",
729 countCertsFound(afNewFoundCerts), RTCrStoreCertCount(hNewStore),
730 countCertsFound(pafFoundCerts), RTCrStoreCertCount(*phStore) ));
731
732 memcpy(pafFoundCerts, afNewFoundCerts, sizeof(afNewFoundCerts));
733 RTCrStoreRelease(*phStore);
734 *phStore = hNewStore;
735 hNewStore = NIL_RTCRSTORE;
736 }
737 else
738 {
739 RT_ZERO(pafFoundCerts);
740 LogRel(("refreshCertificates/#3: RTCrStoreCertExportAsPem unexpectedly failed with %Rrc\n", rc));
741 }
742 }
743 else
744 LogRel(("refreshCertificates/#3: Sticking with the old file, missing essential certs.\n"));
745 }
746 RTCrStoreRelease(hNewStore);
747 }
748 return rc;
749}
750
751/* static */
752DECLCALLBACK(void) UINetworkReplyPrivateThread::handleProgressChange(RTHTTP hHttp, void *pvUser, uint64_t cbDownloadTotal, uint64_t cbDownloaded)
753{
754 /* Redirect callback to particular object: */
755 Q_UNUSED(hHttp);
756 AssertPtrReturnVoid(pvUser);
757 static_cast<UINetworkReplyPrivateThread*>(pvUser)->handleProgressChange(cbDownloadTotal, cbDownloaded);
758}
759
760
761#ifndef VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS
762
763
764/*********************************************************************************************************************************
765* Class UINetworkReplyPrivate implementation. *
766*********************************************************************************************************************************/
767
768UINetworkReplyPrivate::UINetworkReplyPrivate(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders)
769 : m_error(UINetworkReply::NoError)
770 , m_pThread(0)
771{
772 /* Prepare full error template: */
773 m_strErrorTemplate = tr("%1: %2", "Context description: Error description");
774
775 /* Create and run reply thread: */
776 m_pThread = new UINetworkReplyPrivateThread(type, url, strTarget, requestHeaders);
777 connect(m_pThread, &UINetworkReplyPrivateThread::sigDownloadProgress,
778 this, &UINetworkReplyPrivate::downloadProgress, Qt::QueuedConnection);
779 connect(m_pThread, &UINetworkReplyPrivateThread::finished,
780 this, &UINetworkReplyPrivate::sltFinished);
781 m_pThread->start();
782}
783
784UINetworkReplyPrivate::~UINetworkReplyPrivate()
785{
786 /* Terminate network-reply thread: */
787 m_pThread->abort();
788 m_pThread->wait();
789 delete m_pThread;
790 m_pThread = 0;
791}
792
793QString UINetworkReplyPrivate::errorString() const
794{
795 /* Look for known error codes: */
796 switch (m_error)
797 {
798 case UINetworkReply::NoError: break;
799 case UINetworkReply::RemoteHostClosedError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Unable to initialize HTTP library"));
800 case UINetworkReply::UrlNotFoundError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Url not found on the server"));
801 case UINetworkReply::HostNotFoundError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Host not found"));
802 case UINetworkReply::ContentAccessDenied: return m_strErrorTemplate.arg(m_pThread->context(), tr("Content access denied"));
803 case UINetworkReply::ProtocolFailure: return m_strErrorTemplate.arg(m_pThread->context(), tr("Protocol failure"));
804 case UINetworkReply::ConnectionRefusedError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Connection refused"));
805 case UINetworkReply::SslHandshakeFailedError: return m_strErrorTemplate.arg(m_pThread->context(), tr("SSL authentication failed"));
806 case UINetworkReply::AuthenticationRequiredError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Wrong SSL certificate format"));
807 case UINetworkReply::ContentReSendError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Content moved"));
808 case UINetworkReply::ProxyNotFoundError: return m_strErrorTemplate.arg(m_pThread->context(), tr("Proxy not found"));
809 default: return m_strErrorTemplate.arg(m_pThread->context(), tr("Unknown reason"));
810 }
811 /* Return null-string by default: */
812 return QString();
813}
814
815void UINetworkReplyPrivate::sltFinished()
816{
817 /* Look for known error codes: */
818 switch (m_pThread->error())
819 {
820 case VINF_SUCCESS: m_error = UINetworkReply::NoError; break;
821 case VERR_HTTP_INIT_FAILED: m_error = UINetworkReply::RemoteHostClosedError; break;
822 case VERR_HTTP_NOT_FOUND: m_error = UINetworkReply::UrlNotFoundError; break;
823 case VERR_HTTP_HOST_NOT_FOUND: m_error = UINetworkReply::HostNotFoundError; break;
824 case VERR_HTTP_ACCESS_DENIED: m_error = UINetworkReply::ContentAccessDenied; break;
825 case VERR_HTTP_BAD_REQUEST: m_error = UINetworkReply::ProtocolFailure; break;
826 case VERR_HTTP_COULDNT_CONNECT: m_error = UINetworkReply::ConnectionRefusedError; break;
827 case VERR_HTTP_SSL_CONNECT_ERROR: m_error = UINetworkReply::SslHandshakeFailedError; break;
828 case VERR_HTTP_CACERT_WRONG_FORMAT: m_error = UINetworkReply::AuthenticationRequiredError; break;
829 case VERR_HTTP_CACERT_CANNOT_AUTHENTICATE: m_error = UINetworkReply::AuthenticationRequiredError; break;
830 case VERR_HTTP_ABORTED: m_error = UINetworkReply::OperationCanceledError; break;
831 case VERR_HTTP_REDIRECTED: m_error = UINetworkReply::ContentReSendError; break;
832 case VERR_HTTP_PROXY_NOT_FOUND: m_error = UINetworkReply::ProxyNotFoundError; break;
833 default: m_error = UINetworkReply::UnknownNetworkError; break;
834 }
835 /* Redirect signal to external listeners: */
836 emit finished();
837}
838
839
840/*********************************************************************************************************************************
841* Class UINetworkReply implementation. *
842*********************************************************************************************************************************/
843
844UINetworkReply::UINetworkReply(UINetworkRequestType type, const QUrl &url, const QString &strTarget, const UserDictionary &requestHeaders)
845 : m_pReply(new UINetworkReplyPrivate(type, url, strTarget, requestHeaders))
846{
847 /* Prepare network-reply object connections: */
848 connect(m_pReply, &UINetworkReplyPrivate::downloadProgress, this, &UINetworkReply::downloadProgress);
849 connect(m_pReply, &UINetworkReplyPrivate::finished, this, &UINetworkReply::finished);
850}
851
852UINetworkReply::~UINetworkReply()
853{
854 /* Cleanup network-reply object: */
855 if (m_pReply)
856 {
857 delete m_pReply;
858 m_pReply = 0;
859 }
860}
861
862void UINetworkReply::abort()
863{
864 return m_pReply->abort();
865}
866
867QUrl UINetworkReply::url() const
868{
869 return m_pReply->url();
870}
871
872UINetworkReply::NetworkError UINetworkReply::error() const
873{
874 return m_pReply->error();
875}
876
877QString UINetworkReply::errorString() const
878{
879 return m_pReply->errorString();
880}
881
882QByteArray UINetworkReply::readAll() const
883{
884 return m_pReply->readAll();
885}
886
887QVariant UINetworkReply::header(UINetworkReply::KnownHeader header) const
888{
889 return m_pReply->header(header);
890}
891
892#include "UINetworkReply.moc"
893
894#endif /* !VBOX_GUI_IN_TST_SSL_CERT_DOWNLOADS */
895
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