aboutsummaryrefslogtreecommitdiff
path: root/lib/resourceresolver.cpp
blob: 0d5c5a20b4f72c7a50ba309ab8ba4d29e1358316 (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
#include "resourceresolver.h"

#include "settings.h"

#include <QtCore/QRegularExpression>

using namespace Quotient;

QString ResourceResolver::toMatrixId(const QString& uriOrId,
                                     QStringList uriServers)
{
    auto id = QUrl::fromPercentEncoding(uriOrId.toUtf8());
    const auto MatrixScheme = "matrix:"_ls;
    if (id.startsWith(MatrixScheme)) {
        id.remove(0, MatrixScheme.size());
        for (const auto& p: { std::pair { "user/"_ls, '@' },
                              { "roomid/"_ls, '!' },
                              { "room/"_ls, '#' } })
            if (id.startsWith(p.first)) {
                id.replace(0, p.first.size(), p.second);
                break;
            }
        // The below assumes that /event/ cannot show up in normal Matrix ids.
        id.replace("/event/"_ls, "/$"_ls);
    } else {
        const auto MatrixTo_ServerName = QStringLiteral("matrix.to");
        if (!uriServers.contains(MatrixTo_ServerName))
            uriServers.push_back(MatrixTo_ServerName);
        id.remove(
            QRegularExpression("^https://(" + uriServers.join('|') + ")/?#/"));
    }
    return id;
}

ResourceResolver::Result ResourceResolver::visitResource(
    Connection* account, const QString& identifier,
    std::function<void(User*)> userHandler,
    std::function<void(Room*, QString)> roomEventHandler)
{
    const auto& normalizedId = toMatrixId(identifier);
    auto&& [sigil, mainId, secondaryId] = parseIdentifier(normalizedId);
            Room* room = nullptr;
            switch (sigil) {
        case char(-1):
            return MalformedMatrixId;
        case char(0):
            return EmptyMatrixId;
        case '@':
            if (auto* user = account->user(mainId)) {
                userHandler(user);
                return Success;
            }
            return MalformedMatrixId;
        case '!':
            if ((room = account->room(mainId)))
                break;
            return UnknownMatrixId;
        case '#':
            if ((room = account->roomByAlias(mainId)))
                break;
            [[fallthrough]];
        default:
            return UnknownMatrixId;
    }
    roomEventHandler(room, secondaryId);
    return Success;
}

ResourceResolver::IdentifierParts
ResourceResolver::parseIdentifier(const QString& identifier)
{
    if (identifier.isEmpty())
        return {};
    
    // The regex is quick and dirty, only intending to triage the id.
    static const QRegularExpression IdRE {
        "^(?<main>(?<sigil>.)([^/]+))(/(?<sec>[^?]+))?"
    };
    auto dissectedId = IdRE.match(identifier);
    if (!dissectedId.hasMatch())
        return { char(-1) };

    const auto sigil = dissectedId.captured("sigil");
    return { sigil.size() != 1 ? char(-1) : sigil[0].toLatin1(),
                dissectedId.captured("main"), dissectedId.captured("sec") };
}

ResourceResolver::Result
ResourceResolver::openResource(Connection* account, const QString& identifier,
                               const QString& action)
{
    return visitResource(account, identifier,
        [this, &action](User* u) { emit userAction(u, action); },
        [this, &action](Room* room, const QString& eventId) {
            emit roomAction(room, eventId, action);
        });
}