1 /*
2     Copyright © 2014-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 RAWDATABASE_H
21 #define RAWDATABASE_H
22 
23 #include "src/util/strongtype.h"
24 
25 #include <QByteArray>
26 #include <QMutex>
27 #include <QPair>
28 #include <QQueue>
29 #include <QString>
30 #include <QThread>
31 #include <QVariant>
32 #include <QVector>
33 #include <QRegularExpression>
34 
35 #include <atomic>
36 #include <cassert>
37 #include <functional>
38 #include <memory>
39 
40 /// The two following defines are required to use SQLCipher
41 /// They are used by the sqlite3.h header
42 #define SQLITE_HAS_CODEC
43 #define SQLITE_TEMP_STORE 2
44 
45 #include <sqlite3.h>
46 
47 using RowId = NamedType<int64_t, struct RowIdTag, Orderable>;
48 Q_DECLARE_METATYPE(RowId);
49 
50 class RawDatabase : QObject
51 {
52     Q_OBJECT
53 
54 public:
55     class Query
56     {
57     public:
58         Query(QString query, QVector<QByteArray> blobs = {},
59               const std::function<void(RowId)>& insertCallback = {})
60             : query{query.toUtf8()}
61             , blobs{blobs}
62             , insertCallback{insertCallback}
63         {
64         }
Query(QString query,const std::function<void (RowId)> & insertCallback)65         Query(QString query, const std::function<void(RowId)>& insertCallback)
66             : query{query.toUtf8()}
67             , insertCallback{insertCallback}
68         {
69         }
Query(QString query,const std::function<void (const QVector<QVariant> &)> & rowCallback)70         Query(QString query, const std::function<void(const QVector<QVariant>&)>& rowCallback)
71             : query{query.toUtf8()}
72             , rowCallback{rowCallback}
73         {
74         }
75         Query() = default;
76 
77     private:
78         QByteArray query;
79         QVector<QByteArray> blobs;
80         std::function<void(RowId)> insertCallback;
81         std::function<void(const QVector<QVariant>&)> rowCallback;
82         QVector<sqlite3_stmt*> statements;
83 
84         friend class RawDatabase;
85     };
86 
87 public:
88     enum class SqlCipherParams {
89         // keep these sorted in upgrade order
90         p3_0, // SQLCipher 3.0 default encryption params
91         // SQLCipher 4.0 default params where SQLCipher 3.0 supports them, but 3.0 params where not possible.
92         // We accidentally got to this state when attemption to update all databases to 4.0 defaults even when using
93         // SQLCipher 3.x, but might as well keep using these for people with SQLCipher 3.x.
94         halfUpgradedTo4,
95         p4_0 // SQLCipher 4.0 default encryption params
96     };
97 
98     RawDatabase(const QString& path, const QString& password, const QByteArray& salt);
99     ~RawDatabase();
100     bool isOpen();
101 
102     bool execNow(const QString& statement);
103     bool execNow(const Query& statement);
104     bool execNow(const QVector<Query>& statements);
105 
106     void execLater(const QString& statement);
107     void execLater(const Query& statement);
108     void execLater(const QVector<Query>& statements);
109 
110     void sync();
111 
toString(SqlCipherParams params)112     static QString toString(SqlCipherParams params)
113     {
114         switch (params)
115         {
116         case SqlCipherParams::p3_0:
117             return "3.0 default";
118         case SqlCipherParams::halfUpgradedTo4:
119             return "3.x max compatible";
120         case SqlCipherParams::p4_0:
121             return "4.0 default";
122         }
123         assert(false);
124         return {};
125     }
126 
127 public slots:
128     bool setPassword(const QString& password);
129     bool rename(const QString& newPath);
130     bool remove();
131 
132 protected slots:
133     bool open(const QString& path, const QString& hexKey = {});
134     void close();
135     void process();
136 
137 private:
138     QString anonymizeQuery(const QByteArray& query);
139     bool openEncryptedDatabaseAtLatestSupportedVersion(const QString& hexKey);
140     bool updateSavedCipherParameters(const QString& hexKey, SqlCipherParams newParams);
141     bool setCipherParameters(SqlCipherParams params, const QString& database = {});
142     SqlCipherParams highestSupportedParams();
143     SqlCipherParams readSavedCipherParams(const QString& hexKey, SqlCipherParams newParams);
144     bool setKey(const QString& hexKey);
145     int getUserVersion();
146     bool encryptDatabase(const QString& newHexKey);
147     bool decryptDatabase();
148     bool commitDbSwap(const QString& hexKey);
149     bool testUsable();
150 
151 protected:
152     static QString deriveKey(const QString& password, const QByteArray& salt);
153     static QString deriveKey(const QString& password);
154     static QVariant extractData(sqlite3_stmt* stmt, int col);
155     static void regexpInsensitive(sqlite3_context* ctx, int argc, sqlite3_value** argv);
156     static void regexpSensitive(sqlite3_context* ctx, int argc, sqlite3_value** argv);
157 
158 private:
159     static void regexp(sqlite3_context* ctx, int argc, sqlite3_value** argv, const QRegularExpression::PatternOptions cs);
160 
161     struct Transaction
162     {
163         QVector<Query> queries;
164         std::atomic_bool* success = nullptr;
165         std::atomic_bool* done = nullptr;
166     };
167 
168 private:
169     sqlite3* sqlite;
170     std::unique_ptr<QThread> workerThread;
171     QQueue<Transaction> pendingTransactions;
172     QMutex transactionsMutex;
173     QString path;
174     QByteArray currentSalt;
175     QString currentHexKey;
176 };
177 
178 #endif // RAWDATABASE_H
179