Persist Blowfish keys in the database
[quassel.git] / src / core / abstractsqlstorage.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #pragma once
22
23 #include "storage.h"
24
25 #include <memory>
26
27 #include <QSqlDatabase>
28 #include <QSqlQuery>
29 #include <QSqlError>
30
31 class AbstractSqlMigrationReader;
32 class AbstractSqlMigrationWriter;
33
34 class AbstractSqlStorage : public Storage
35 {
36     Q_OBJECT
37
38 public:
39     AbstractSqlStorage(QObject *parent = 0);
40     virtual ~AbstractSqlStorage();
41
42     virtual std::unique_ptr<AbstractSqlMigrationReader> createMigrationReader() { return {}; }
43     virtual std::unique_ptr<AbstractSqlMigrationWriter> createMigrationWriter() { return {}; }
44
45 public slots:
46     virtual State init(const QVariantMap &settings = QVariantMap());
47     virtual bool setup(const QVariantMap &settings = QVariantMap());
48
49 protected:
50     inline virtual void sync() {};
51
52     QSqlDatabase logDb();
53
54     /**
55      * Fetch an SQL query string by name and optional schema version
56      *
57      * Loads the named SQL query from the built-in SQL resource collection, returning it as a
58      * string.  If a version is specified, it'll be loaded from the schema version-specific folder
59      * instead.
60      *
61      * @see schemaVersion()
62      *
63      * @param[in] queryName  File name of the SQL query, minus the .sql extension
64      * @param[in] version
65      * @parblock
66      * SQL schema version; if 0, fetches from current version, otherwise loads from the specified
67      * schema version instead of the current schema files.
68      * @endparblock
69      * @return String with the requested SQL query, ready for parameter substitution
70      */
71     QString queryString(const QString &queryName, int version = 0);
72
73     QStringList setupQueries();
74
75     QStringList upgradeQueries(int ver);
76     bool upgradeDb();
77
78     bool watchQuery(QSqlQuery &query);
79
80     int schemaVersion();
81     virtual int installedSchemaVersion() { return -1; };
82     virtual bool updateSchemaVersion(int newVersion) = 0;
83     virtual bool setupSchemaVersion(int version) = 0;
84
85     virtual void setConnectionProperties(const QVariantMap &properties) = 0;
86     virtual QString driverName() = 0;
87     inline virtual QString hostName() { return QString(); }
88     inline virtual int port() { return -1; }
89     virtual QString databaseName() = 0;
90     inline virtual QString userName() { return QString(); }
91     inline virtual QString password() { return QString(); }
92
93     //! Initialize db specific features on connect
94     /** This is called every time a connection to a specific SQL backend is established
95      *  the default implementation does nothing.
96      *
97      *  When reimplementing this method, don't use logDB() inside this function as
98      *  this would cause as we're just about to initialize that DB connection.
99      */
100     inline virtual bool initDbSession(QSqlDatabase & /* db */) { return true; }
101
102 private slots:
103     void connectionDestroyed();
104
105 private:
106     void addConnectionToPool();
107     void dbConnect(QSqlDatabase &db);
108
109     int _schemaVersion;
110     bool _debug;
111
112     static int _nextConnectionId;
113     QMutex _connectionPoolMutex;
114     // we let a Connection Object manage each actual db connection
115     // those objects reside in the thread the connection belongs to
116     // which allows us thread safe termination of a connection
117     class Connection;
118     QHash<QThread *, Connection *> _connectionPool;
119 };
120
121
122 // ========================================
123 //  AbstractSqlStorage::Connection
124 // ========================================
125 class AbstractSqlStorage::Connection : public QObject
126 {
127     Q_OBJECT
128
129 public:
130     Connection(const QString &name, QObject *parent = 0);
131     ~Connection();
132
133     inline QLatin1String name() const { return QLatin1String(_name); }
134
135 private:
136     QByteArray _name;
137 };
138
139
140 // ========================================
141 //  AbstractSqlMigrator
142 // ========================================
143 class AbstractSqlMigrator
144 {
145 public:
146     // migration objects
147     struct QuasselUserMO {
148         UserId id;
149         QString username;
150         QString password;
151         int hashversion;
152         QString authenticator;
153     };
154
155     struct SenderMO {
156         int senderId;
157         QString sender;
158         SenderMO() : senderId(0) {}
159     };
160
161     struct IdentityMO {
162         IdentityId id;
163         UserId userid;
164         QString identityname;
165         QString realname;
166         QString awayNick;
167         bool awayNickEnabled;
168         QString awayReason;
169         bool awayReasonEnabled;
170         bool autoAwayEnabled;
171         int autoAwayTime;
172         QString autoAwayReason;
173         bool autoAwayReasonEnabled;
174         bool detachAwayEnabled;
175         QString detachAwayReason;
176         bool detchAwayReasonEnabled;
177         QString ident;
178         QString kickReason;
179         QString partReason;
180         QString quitReason;
181         QByteArray sslCert;
182         QByteArray sslKey;
183     };
184
185     struct IdentityNickMO {
186         int nickid;
187         IdentityId identityId;
188         QString nick;
189     };
190
191     struct NetworkMO {
192         NetworkId networkid;
193         UserId userid;
194         QString networkname;
195         IdentityId identityid;
196         QString encodingcodec;
197         QString decodingcodec;
198         QString servercodec;
199         bool userandomserver;
200         QString perform;
201         bool useautoidentify;
202         QString autoidentifyservice;
203         QString autoidentifypassword;
204         bool useautoreconnect;
205         int autoreconnectinterval;
206         int autoreconnectretries;
207         bool unlimitedconnectretries;
208         bool rejoinchannels;
209         // Custom rate limiting
210         bool usecustommessagerate;
211         int messagerateburstsize;
212         int messageratedelay;
213         bool unlimitedmessagerate;
214         // ...
215         bool connected;
216         QString usermode;
217         QString awaymessage;
218         QString attachperform;
219         QString detachperform;
220         bool usesasl;
221         QString saslaccount;
222         QString saslpassword;
223     };
224
225     struct BufferMO {
226         BufferId bufferid;
227         UserId userid;
228         int groupid;
229         NetworkId networkid;
230         QString buffername;
231         QString buffercname;
232         int buffertype;
233         int lastmsgid;
234         int lastseenmsgid;
235         int markerlinemsgid;
236         int bufferactivity;
237         QString key;
238         bool joined;
239         QString cipher;
240     };
241
242     struct BacklogMO {
243         MsgId messageid;
244         QDateTime time; // has to be in UTC!
245         BufferId bufferid;
246         int type;
247         int flags;
248         int senderid;
249         QString senderprefixes;
250         QString message;
251     };
252
253     struct IrcServerMO {
254         int serverid;
255         UserId userid;
256         NetworkId networkid;
257         QString hostname;
258         int port;
259         QString password;
260         bool ssl;
261         bool sslverify;     /// If true, validate SSL certificates
262         int sslversion;
263         bool useproxy;
264         int proxytype;
265         QString proxyhost;
266         int proxyport;
267         QString proxyuser;
268         QString proxypass;
269     };
270
271     struct UserSettingMO {
272         UserId userid;
273         QString settingname;
274         QByteArray settingvalue;
275     };
276
277     enum MigrationObject {
278         QuasselUser,
279         Sender,
280         Identity,
281         IdentityNick,
282         Network,
283         Buffer,
284         Backlog,
285         IrcServer,
286         UserSetting
287     };
288
289     AbstractSqlMigrator();
290     virtual ~AbstractSqlMigrator() {}
291
292     static QString migrationObject(MigrationObject moType);
293
294 protected:
295     void newQuery(const QString &query, QSqlDatabase db);
296     virtual void resetQuery();
297     virtual bool prepareQuery(MigrationObject mo) = 0;
298     bool exec();
299     inline bool next() { return _query->next(); }
300     inline QVariant value(int index) { return _query->value(index); }
301     inline void bindValue(const QString &placeholder, const QVariant &val) { _query->bindValue(placeholder, val); }
302     inline void bindValue(int pos, const QVariant &val) { _query->bindValue(pos, val); }
303
304     inline QSqlError lastError() { return _query ? _query->lastError() : QSqlError(); }
305     void dumpStatus();
306     inline QString executedQuery() { return _query ? _query->executedQuery() : QString(); }
307     inline QVariantList boundValues();
308
309     virtual bool transaction() = 0;
310     virtual void rollback() = 0;
311     virtual bool commit() = 0;
312
313 private:
314     QSqlQuery *_query;
315 };
316
317
318 class AbstractSqlMigrationReader : public AbstractSqlMigrator
319 {
320 public:
321     AbstractSqlMigrationReader();
322
323     virtual bool readMo(QuasselUserMO &user) = 0;
324     virtual bool readMo(IdentityMO &identity) = 0;
325     virtual bool readMo(IdentityNickMO &identityNick) = 0;
326     virtual bool readMo(NetworkMO &network) = 0;
327     virtual bool readMo(BufferMO &buffer) = 0;
328     virtual bool readMo(SenderMO &sender) = 0;
329     virtual bool readMo(BacklogMO &backlog) = 0;
330     virtual bool readMo(IrcServerMO &ircserver) = 0;
331     virtual bool readMo(UserSettingMO &userSetting) = 0;
332
333     bool migrateTo(AbstractSqlMigrationWriter *writer);
334
335 private:
336     void abortMigration(const QString &errorMsg = QString());
337     bool finalizeMigration();
338
339     template<typename T> bool transferMo(MigrationObject moType, T &mo);
340
341     AbstractSqlMigrationWriter *_writer;
342 };
343
344
345 class AbstractSqlMigrationWriter : public AbstractSqlMigrator
346 {
347 public:
348     virtual bool writeMo(const QuasselUserMO &user) = 0;
349     virtual bool writeMo(const IdentityMO &identity) = 0;
350     virtual bool writeMo(const IdentityNickMO &identityNick) = 0;
351     virtual bool writeMo(const NetworkMO &network) = 0;
352     virtual bool writeMo(const BufferMO &buffer) = 0;
353     virtual bool writeMo(const SenderMO &sender) = 0;
354     virtual bool writeMo(const BacklogMO &backlog) = 0;
355     virtual bool writeMo(const IrcServerMO &ircserver) = 0;
356     virtual bool writeMo(const UserSettingMO &userSetting) = 0;
357
358     inline bool migrateFrom(AbstractSqlMigrationReader *reader) { return reader->migrateTo(this); }
359
360     // called after migration process
361     virtual inline bool postProcess() { return true; }
362     friend class AbstractSqlMigrationReader;
363 };