1 /*
2     Copyright © 2015-2019 by The qTox Project Contributors
3 
4     This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6     qTox is libre software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     qTox is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with qTox.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef HISTORY_H
21 #define HISTORY_H
22 
23 #include <QDateTime>
24 #include <QHash>
25 #include <QPointer>
26 #include <QVector>
27 
28 #include <cassert>
29 #include <cstdint>
30 #include <tox/toxencryptsave.h>
31 
32 #include "src/core/toxfile.h"
33 #include "src/core/toxpk.h"
34 #include "src/persistence/db/rawdatabase.h"
35 #include "src/widget/searchtypes.h"
36 
37 class Profile;
38 class HistoryKeeper;
39 
40 enum class HistMessageContentType
41 {
42     message,
43     file
44 };
45 
46 class HistMessageContent
47 {
48 public:
HistMessageContent(QString message)49     HistMessageContent(QString message)
50         : data(std::make_shared<QString>(std::move(message)))
51         , type(HistMessageContentType::message)
52     {}
53 
HistMessageContent(ToxFile file)54     HistMessageContent(ToxFile file)
55         : data(std::make_shared<ToxFile>(std::move(file)))
56         , type(HistMessageContentType::file)
57     {}
58 
getType()59     HistMessageContentType getType() const
60     {
61         return type;
62     }
63 
asMessage()64     QString& asMessage()
65     {
66         assert(type == HistMessageContentType::message);
67         return *static_cast<QString*>(data.get());
68     }
69 
asFile()70     ToxFile& asFile()
71     {
72         assert(type == HistMessageContentType::file);
73         return *static_cast<ToxFile*>(data.get());
74     }
75 
asMessage()76     const QString& asMessage() const
77     {
78         assert(type == HistMessageContentType::message);
79         return *static_cast<QString*>(data.get());
80     }
81 
asFile()82     const ToxFile& asFile() const
83     {
84         assert(type == HistMessageContentType::file);
85         return *static_cast<ToxFile*>(data.get());
86     }
87 
88 private:
89     // Not really shared but shared_ptr has support for shared_ptr<void>
90     std::shared_ptr<void> data;
91     HistMessageContentType type;
92 };
93 
94 struct FileDbInsertionData
95 {
96     FileDbInsertionData();
97 
98     RowId historyId;
99     QString friendPk;
100     QString fileId;
101     QString fileName;
102     QString filePath;
103     int64_t size;
104     int direction;
105 };
106 Q_DECLARE_METATYPE(FileDbInsertionData);
107 
108 enum class MessageState
109 {
110     complete,
111     pending,
112     broken
113 };
114 
115 class History : public QObject, public std::enable_shared_from_this<History>
116 {
117     Q_OBJECT
118 public:
119     struct HistMessage
120     {
HistMessageHistMessage121         HistMessage(RowId id, MessageState state, QDateTime timestamp, QString chat, QString dispName,
122                     QString sender, QString message)
123             : chat{chat}
124             , sender{sender}
125             , dispName{dispName}
126             , timestamp{timestamp}
127             , id{id}
128             , state{state}
129             , content(std::move(message))
130         {}
131 
HistMessageHistMessage132         HistMessage(RowId id, MessageState state, QDateTime timestamp, QString chat, QString dispName,
133                     QString sender, ToxFile file)
134             : chat{chat}
135             , sender{sender}
136             , dispName{dispName}
137             , timestamp{timestamp}
138             , id{id}
139             , state{state}
140             , content(std::move(file))
141         {}
142 
143 
144         QString chat;
145         QString sender;
146         QString dispName;
147         QDateTime timestamp;
148         RowId id;
149         MessageState state;
150         HistMessageContent content;
151     };
152 
153     struct DateIdx
154     {
155         QDate date;
156         size_t numMessagesIn;
157     };
158 
159 public:
160     explicit History(std::shared_ptr<RawDatabase> db);
161     ~History();
162 
163     bool isValid();
164 
165     bool historyExists(const ToxPk& friendPk);
166 
167     void eraseHistory();
168     void removeFriendHistory(const QString& friendPk);
169     void addNewMessage(const QString& friendPk, const QString& message, const QString& sender,
170                        const QDateTime& time, bool isDelivered, QString dispName,
171                        const std::function<void(RowId)>& insertIdCallback = {});
172 
173     void addNewFileMessage(const QString& friendPk, const QString& fileId,
174                            const QString& fileName, const QString& filePath, int64_t size,
175                            const QString& sender, const QDateTime& time, QString const& dispName);
176 
177     void setFileFinished(const QString& fileId, bool success, const QString& filePath, const QByteArray& fileHash);
178     size_t getNumMessagesForFriend(const ToxPk& friendPk);
179     size_t getNumMessagesForFriendBeforeDate(const ToxPk& friendPk, const QDateTime& date);
180     QList<HistMessage> getMessagesForFriend(const ToxPk& friendPk, size_t firstIdx, size_t lastIdx);
181     QList<HistMessage> getUndeliveredMessagesForFriend(const ToxPk& friendPk);
182     QDateTime getDateWhereFindPhrase(const QString& friendPk, const QDateTime& from, QString phrase,
183                                      const ParameterSearch& parameter);
184     QList<DateIdx> getNumMessagesForFriendBeforeDateBoundaries(const ToxPk& friendPk,
185                                                                const QDate& from, size_t maxNum);
186 
187     void markAsDelivered(RowId messageId);
188 
189 protected:
190     QVector<RawDatabase::Query>
191     generateNewMessageQueries(const QString& friendPk, const QString& message,
192                               const QString& sender, const QDateTime& time, bool isDelivered,
193                               QString dispName, std::function<void(RowId)> insertIdCallback = {});
194 
195 signals:
196     void fileInsertionReady(FileDbInsertionData data);
197     void fileInserted(RowId dbId, QString fileId);
198 
199 private slots:
200     void onFileInsertionReady(FileDbInsertionData data);
201     void onFileInserted(RowId dbId, QString fileId);
202 
203 private:
204     bool historyAccessBlocked();
205     static RawDatabase::Query generateFileFinished(RowId fileId, bool success,
206                                                    const QString& filePath, const QByteArray& fileHash);
207     std::shared_ptr<RawDatabase> db;
208 
209 
210     QHash<QString, int64_t> peers;
211     struct FileInfo
212     {
213         bool finished = false;
214         bool success = false;
215         QString filePath;
216         QByteArray fileHash;
217         RowId fileId{-1};
218     };
219 
220     // This needs to be a shared pointer to avoid callback lifetime issues
221     QHash<QString, FileInfo> fileInfos;
222 };
223 
224 #endif // HISTORY_H
225