1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5
6 #include "mumble_pch.hpp"
7
8 #include "WebFetch.h"
9
10 #include "Global.h"
11 #include "NetworkConfig.h"
12
WebFetch(QString service,QUrl url,QObject * obj,const char * slot)13 WebFetch::WebFetch(QString service, QUrl url, QObject *obj, const char *slot)
14 : QObject()
15 , qoObject(obj)
16 , cpSlot(slot)
17 , m_service(service) {
18
19 url.setScheme(QLatin1String("https"));
20
21 if (!g.s.qsServicePrefix.isEmpty()) {
22 url.setHost(prefixedServiceHost());
23 } else {
24 url.setHost(serviceHost());
25 }
26
27 qnr = Network::get(url);
28 connect(qnr, SIGNAL(finished()), this, SLOT(finished()));
29 connect(this, SIGNAL(fetched(QByteArray,QUrl,QMap<QString,QString>)), obj, slot);
30 }
31
prefixedServiceHost() const32 QString WebFetch::prefixedServiceHost() const {
33 if (g.s.qsServicePrefix.isEmpty()) {
34 return serviceHost();
35 }
36 return QString::fromLatin1("%1-%2.mumble.info").arg(g.s.qsServicePrefix, m_service);
37 }
38
serviceHost() const39 QString WebFetch::serviceHost() const {
40 return QString::fromLatin1("%1.mumble.info").arg(m_service);
41 }
42
fromUtf8(const QByteArray & qba)43 static QString fromUtf8(const QByteArray &qba) {
44 if (qba.isEmpty())
45 return QString();
46 return QString::fromUtf8(qba.constData(), qba.length());
47 }
48
finished()49 void WebFetch::finished() {
50 // Note that if this functions succeeds, it should deleteLater() itself, as this is a temporary object.
51 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()) == qnr);
52 qnr->disconnect();
53 qnr->deleteLater();
54
55 QUrl url = qnr->request().url();
56
57 if (qnr->error() == QNetworkReply::NoError) {
58 QByteArray a = qnr->readAll();
59
60 // empty response is not an error
61 if (a.isNull())
62 a.append("");
63
64 QMap<QString, QString> headers;
65
66 foreach(const QByteArray &headerName, qnr->rawHeaderList()) {
67 QString name = fromUtf8(headerName);
68 QString value = fromUtf8(qnr->rawHeader(headerName));
69 if (! name.isEmpty() && ! value.isEmpty()) {
70 headers.insert(name, value);
71 if (name == QLatin1String("Use-Service-Prefix")) {
72 QRegExp servicePrefixRegExp(QLatin1String("^[a-zA-Z]+$"));
73 if (servicePrefixRegExp.exactMatch(value)) {
74 g.s.qsServicePrefix = value.toLower();
75 }
76 }
77 }
78 }
79
80 emit fetched(a, url, headers);
81 deleteLater();
82 } else if (url.host() == prefixedServiceHost() && url.host() != serviceHost()) {
83 // We have tried to fetch from a local service domain (e.g. de-update.mumble.info)
84 // which has failed, so naturally we want to try the non-local one (update.mumble.info)
85 // as well as maybe that one will work.
86 // This of course only makes sense, if prefixedServiceHost() and serviceHost() are in fact
87 // different hosts.
88 url.setHost(serviceHost());
89
90 qnr = Network::get(url);
91 connect(qnr, SIGNAL(finished()), this, SLOT(finished()));
92 } else {
93 emit fetched(QByteArray(), url, QMap<QString,QString>());
94 deleteLater();
95 }
96 }
97
98 /**
99 * @brief Fetch URL from mumble servers.
100 *
101 * If fetching fails, the slot is invoked with a null QByteArray.
102 * @param url URL to fetch. Hostname and scheme must be blank.
103 * @param obj Object to invoke slot on.
104 * @param slot Slot to be triggered, invoked with the signature of \link fetched.
105 */
fetch(const QString & service,const QUrl & url,QObject * obj,const char * slot)106 void WebFetch::fetch(const QString &service, const QUrl &url, QObject *obj, const char *slot) {
107 Q_ASSERT(!service.isEmpty());
108 Q_ASSERT(url.scheme().isEmpty());
109 Q_ASSERT(url.host().isEmpty());
110 Q_ASSERT(obj);
111 Q_ASSERT(slot);
112
113 new WebFetch(service, url, obj, slot);
114 }
115