aboutsummaryrefslogtreecommitdiff
path: root/lib/networkaccessmanager.cpp
blob: 293538eef0ced93235448c1a8bbed9e6860c4f3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "networkaccessmanager.h"

#include "connection.h"
#include "room.h"
#include "accountregistry.h"
#include "mxcreply.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QThreadStorage>
#include <QtCore/QSettings>
#include <QtNetwork/QNetworkReply>

using namespace Quotient;

class NetworkAccessManager::Private {
public:
    explicit Private(NetworkAccessManager* q)
        : q(q)
    {}

    QNetworkReply* createImplRequest(Operation op,
                                     const QNetworkRequest& outerRequest,
                                     Connection* connection)
    {
        Q_ASSERT(outerRequest.url().scheme() == "mxc");
        QNetworkRequest r(outerRequest);
        r.setUrl(QUrl(QStringLiteral("%1/_matrix/media/r0/download/%2")
                          .arg(connection->homeserver().toString(),
                               outerRequest.url().authority()
                                   + outerRequest.url().path())));
        return q->createRequest(op, r);
    }

    NetworkAccessManager* q;
    QList<QSslError> ignoredSslErrors;
};

NetworkAccessManager::NetworkAccessManager(QObject* parent)
    : QNetworkAccessManager(parent), d(std::make_unique<Private>(this))
{
    connect(this, &QNetworkAccessManager::sslErrors, this, [](QNetworkReply *reply, const QList<QSslError> &errors) {
        reply->ignoreSslErrors();
    });
}

QList<QSslError> NetworkAccessManager::ignoredSslErrors() const
{
    return d->ignoredSslErrors;
}

void NetworkAccessManager::addIgnoredSslError(const QSslError& error)
{
    d->ignoredSslErrors << error;
}

void NetworkAccessManager::clearIgnoredSslErrors()
{
    d->ignoredSslErrors.clear();
}

static NetworkAccessManager* createNam()
{
    auto nam = new NetworkAccessManager();
#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
    // See #109; in newer Qt, bearer management is deprecated altogether
    NetworkAccessManager::connect(nam,
        &QNetworkAccessManager::networkAccessibleChanged, [nam] {
            nam->setNetworkAccessible(QNetworkAccessManager::Accessible);
        });
#endif
    return nam;
}

NetworkAccessManager* NetworkAccessManager::instance()
{
    static QThreadStorage<NetworkAccessManager*> storage;
    if(!storage.hasLocalData()) {
        storage.setLocalData(createNam());
    }
    return storage.localData();
}

NetworkAccessManager::~NetworkAccessManager() = default;

QNetworkReply* NetworkAccessManager::createRequest(
    Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
{
    const auto& mxcUrl = request.url();
    if (mxcUrl.scheme() == "mxc") {
        const QUrlQuery query(mxcUrl.query());
        const auto accountId = query.queryItemValue(QStringLiteral("user_id"));
        if (accountId.isEmpty()) {
            // Using QSettings here because Quotient::NetworkSettings
            // doesn't provide multithreading guarantees
            static thread_local QSettings s;
            if (!s.value("Network/allow_direct_media_requests").toBool()) {
                qCWarning(NETWORK) << "No connection specified";
                return new MxcReply();
            }
            // TODO: Make the best effort with a direct unauthenticated request
            // to the media server
        } else {
            auto* const connection = AccountRegistry::instance().get(accountId);
            if (!connection) {
                qCWarning(NETWORK) << "Connection" << accountId << "not found";
                return new MxcReply();
            }
            const auto roomId = query.queryItemValue(QStringLiteral("room_id"));
            if (!roomId.isEmpty()) {
                auto room = connection->room(roomId);
                if (!room) {
                    qCWarning(NETWORK) << "Room" << roomId << "not found";
                    return new MxcReply();
                }
                return new MxcReply(
                    d->createImplRequest(op, request, connection), room,
                    query.queryItemValue(QStringLiteral("event_id")));
            }
            return new MxcReply(
                d->createImplRequest(op, request, connection));
        }
    }
    auto reply = QNetworkAccessManager::createRequest(op, request, outgoingData);
    reply->ignoreSslErrors(d->ignoredSslErrors);
    return reply;
}

QStringList NetworkAccessManager::supportedSchemesImplementation() const
{
    auto schemes = QNetworkAccessManager::supportedSchemesImplementation();
    schemes += QStringLiteral("mxc");
    return schemes;
}