summary refs log tree commit diff
path: root/src/timeline/EventStore.h
blob: f2f9e2d742e2fb9228be55d50ab78a98f7e39515 (plain) (blame)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <limits>
#include <string>

#include <QCache>
#include <QObject>
#include <QVariant>

#include <mtx/events/collections.hpp>
#include <mtx/responses/messages.hpp>
#include <mtx/responses/sync.hpp>
#include <mtx/common.hpp>

#include "Reaction.h"
#include "encryption/Olm.h"

class EventStore final : public QObject
{
    Q_OBJECT

public:
    EventStore(std::string room_id, QObject *parent);

    void refetchOnlineKeyBackupKeys();

    // taken from QtPrivate::QHashCombine
    static uint hashCombine(uint hash, uint seed)
    {
        return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2));
    };
    struct Index
    {
        std::string room;
        uint64_t idx;

        friend uint qHash(const Index &i, uint seed = 0) noexcept
        {
            seed = hashCombine(qHashBits(i.room.data(), (int)i.room.size(), seed), seed);
            seed = hashCombine(qHash(i.idx, seed), seed);
            return seed;
        }

        friend bool operator==(const Index &a, const Index &b) noexcept
        {
            return a.idx == b.idx && a.room == b.room;
        }
    };
    struct IdIndex
    {
        std::string room, id;

        friend uint qHash(const IdIndex &i, uint seed = 0) noexcept
        {
            seed = hashCombine(qHashBits(i.room.data(), (int)i.room.size(), seed), seed);
            seed = hashCombine(qHashBits(i.id.data(), (int)i.id.size(), seed), seed);
            return seed;
        }

        friend bool operator==(const IdIndex &a, const IdIndex &b) noexcept
        {
            return a.id == b.id && a.room == b.room;
        }
    };

    void fetchMore();
    void handleSync(const mtx::responses::Timeline &events);

    // optionally returns the event or nullptr and fetches it, after which it emits a
    // relatedFetched event
    mtx::events::collections::TimelineEvents const *get(const std::string &id,
                                                        std::string_view related_to,
                                                        bool decrypt       = true,
                                                        bool resolve_edits = true);
    // always returns a proper event as long as the idx is valid
    mtx::events::collections::TimelineEvents const *get(int idx, bool decrypt = true);

    QVariantList reactions(const std::string &event_id);
    std::vector<mtx::events::collections::TimelineEvents> edits(const std::string &event_id);
    olm::DecryptionErrorCode decryptionError(std::string id);
    void
    requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev, bool manual);

    int size() const
    {
        return (last != std::numeric_limits<uint64_t>::max() && last >= first)
                 ? static_cast<int>(last - first) + 1
                 : 0;
    }
    int toExternalIdx(uint64_t idx) const { return static_cast<int>(idx - first); }
    uint64_t toInternalIdx(int idx) const { return first + idx; }

    std::optional<int> idToIndex(std::string_view id) const;
    std::optional<std::string> indexToId(int idx) const;

signals:
    void beginInsertRows(int from, int to);
    void endInsertRows();
    void beginResetModel();
    void endResetModel();
    void dataChanged(int from, int to);
    void pinsChanged();
    void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
    void eventFetched(std::string id,
                      std::string relatedTo,
                      const mtx::events::collections::TimelineEvents& timeline);
    void oldMessagesRetrieved(const mtx::responses::Messages &);
    void fetchedMore();

    void processPending();
    void messageSent(std::string txn_id, std::string event_id);
    void messageFailed(std::string txn_id);
    void startDMVerification(
      const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg);
    void updateFlowEventId(std::string event_id);

public slots:
    void addPending(const mtx::events::collections::TimelineEvents& event);
    void receivedSessionKey(const std::string &session_id);
    void clearTimeline();
    void enableKeyRequests(bool suppressKeyRequests_);

private:
    olm::DecryptionResult const *
    decryptEvent(const IdIndex &idx,
                 const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e);

    std::string room_id_;

    uint64_t first = std::numeric_limits<uint64_t>::max(),
             last  = std::numeric_limits<uint64_t>::max();

    static QCache<IdIndex, olm::DecryptionResult> decryptedEvents_;
    static QCache<Index, mtx::events::collections::TimelineEvents> events_;
    static QCache<IdIndex, mtx::events::collections::TimelineEvents> events_by_id_;

    struct PendingKeyRequests
    {
        std::string request_id;
        std::vector<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>> events;
        qint64 requested_at;
    };
    std::map<std::string, PendingKeyRequests> pending_key_requests;

    std::string current_txn;
    int current_txn_error_count = 0;
    bool noMoreMessages         = false;
    bool suppressKeyRequests    = true;
};