aboutsummaryrefslogtreecommitdiff
path: root/lib/networkaccessmanager.cpp
blob: 3b0dc92b7ef83957bd7f11c2b51844ef8e9cd7cd (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
// 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))
{}

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(QCoreApplication::instance());
#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;
    // FIXME: createNam() returns an object parented to
    // QCoreApplication::instance() that lives in the main thread
    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()) {
                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 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 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;
}