core: Store session state in database, migrate old
[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 struct SenderData {
122     QString sender;
123     QString realname;
124     QString avatarurl;
125
126     friend uint qHash(const SenderData &key);
127     friend bool operator==(const SenderData &a, const SenderData &b);
128 };
129
130 // ========================================
131 //  AbstractSqlStorage::Connection
132 // ========================================
133 class AbstractSqlStorage::Connection : public QObject
134 {
135     Q_OBJECT
136
137 public:
138     Connection(const QString &name, QObject *parent = 0);
139     ~Connection();
140
141     inline QLatin1String name() const { return QLatin1String(_name); }
142
143 private:
144     QByteArray _name;
145 };
146
147
148 // ========================================
149 //  AbstractSqlMigrator
150 // ========================================
151 class AbstractSqlMigrator
152 {
153 public:
154     // migration objects
155     struct QuasselUserMO {
156         UserId id;
157         QString username;
158         QString password;
159         int hashversion;
160         QString authenticator;
161     };
162
163     struct SenderMO {
164         int senderId;
165         QString sender;
166         QString realname;
167         QString avatarurl;
168         SenderMO() : senderId(0) {}
169     };
170
171     struct IdentityMO {
172         IdentityId id;
173         UserId userid;
174         QString identityname;
175         QString realname;
176         QString awayNick;
177         bool awayNickEnabled;
178         QString awayReason;
179         bool awayReasonEnabled;
180         bool autoAwayEnabled;
181         int autoAwayTime;
182         QString autoAwayReason;
183         bool autoAwayReasonEnabled;
184         bool detachAwayEnabled;
185         QString detachAwayReason;
186         bool detchAwayReasonEnabled;
187         QString ident;
188         QString kickReason;
189         QString partReason;
190         QString quitReason;
191         QByteArray sslCert;
192         QByteArray sslKey;
193     };
194
195     struct IdentityNickMO {
196         int nickid;
197         IdentityId identityId;
198         QString nick;
199     };
200
201     struct NetworkMO {
202         NetworkId networkid;
203         UserId userid;
204         QString networkname;
205         IdentityId identityid;
206         QString encodingcodec;
207         QString decodingcodec;
208         QString servercodec;
209         bool userandomserver;
210         QString perform;
211         bool useautoidentify;
212         QString autoidentifyservice;
213         QString autoidentifypassword;
214         bool useautoreconnect;
215         int autoreconnectinterval;
216         int autoreconnectretries;
217         bool unlimitedconnectretries;
218         bool rejoinchannels;
219         // Custom rate limiting
220         bool usecustommessagerate;
221         int messagerateburstsize;
222         int messageratedelay;
223         bool unlimitedmessagerate;
224         // ...
225         bool connected;
226         QString usermode;
227         QString awaymessage;
228         QString attachperform;
229         QString detachperform;
230         bool usesasl;
231         QString saslaccount;
232         QString saslpassword;
233     };
234
235     struct BufferMO {
236         BufferId bufferid;
237         UserId userid;
238         int groupid;
239         NetworkId networkid;
240         QString buffername;
241         QString buffercname;
242         int buffertype;
243         int lastmsgid;
244         int lastseenmsgid;
245         int markerlinemsgid;
246         int bufferactivity;
247         int highlightcount;
248         QString key;
249         bool joined;
250         QString cipher;
251     };
252
253     struct BacklogMO {
254         MsgId messageid;
255         QDateTime time; // has to be in UTC!
256         BufferId bufferid;
257         int type;
258         int flags;
259         int senderid;
260         QString senderprefixes;
261         QString message;
262     };
263
264     struct IrcServerMO {
265         int serverid;
266         UserId userid;
267         NetworkId networkid;
268         QString hostname;
269         int port;
270         QString password;
271         bool ssl;
272         bool sslverify;     /// If true, validate SSL certificates
273         int sslversion;
274         bool useproxy;
275         int proxytype;
276         QString proxyhost;
277         int proxyport;
278         QString proxyuser;
279         QString proxypass;
280     };
281
282     struct UserSettingMO {
283         UserId userid;
284         QString settingname;
285         QByteArray settingvalue;
286     };
287
288     struct CoreStateMO {
289         QString key;
290         QByteArray value;
291     };
292
293     enum MigrationObject {
294         QuasselUser,
295         Sender,
296         Identity,
297         IdentityNick,
298         Network,
299         Buffer,
300         Backlog,
301         IrcServer,
302         UserSetting,
303         CoreState
304     };
305
306     AbstractSqlMigrator();
307     virtual ~AbstractSqlMigrator() {}
308
309     static QString migrationObject(MigrationObject moType);
310
311 protected:
312     void newQuery(const QString &query, QSqlDatabase db);
313     virtual void resetQuery();
314     virtual bool prepareQuery(MigrationObject mo) = 0;
315     bool exec();
316     inline bool next() { return _query->next(); }
317     inline QVariant value(int index) { return _query->value(index); }
318     inline void bindValue(const QString &placeholder, const QVariant &val) { _query->bindValue(placeholder, val); }
319     inline void bindValue(int pos, const QVariant &val) { _query->bindValue(pos, val); }
320
321     inline QSqlError lastError() { return _query ? _query->lastError() : QSqlError(); }
322     void dumpStatus();
323     inline QString executedQuery() { return _query ? _query->executedQuery() : QString(); }
324     inline QVariantList boundValues();
325
326     virtual bool transaction() = 0;
327     virtual void rollback() = 0;
328     virtual bool commit() = 0;
329
330 private:
331     QSqlQuery *_query;
332 };
333
334
335 class AbstractSqlMigrationReader : public AbstractSqlMigrator
336 {
337 public:
338     AbstractSqlMigrationReader();
339
340     virtual bool readMo(QuasselUserMO &user) = 0;
341     virtual bool readMo(IdentityMO &identity) = 0;
342     virtual bool readMo(IdentityNickMO &identityNick) = 0;
343     virtual bool readMo(NetworkMO &network) = 0;
344     virtual bool readMo(BufferMO &buffer) = 0;
345     virtual bool readMo(SenderMO &sender) = 0;
346     virtual bool readMo(BacklogMO &backlog) = 0;
347     virtual bool readMo(IrcServerMO &ircserver) = 0;
348     virtual bool readMo(UserSettingMO &userSetting) = 0;
349     virtual bool readMo(CoreStateMO &coreState) = 0;
350
351     bool migrateTo(AbstractSqlMigrationWriter *writer);
352
353 private:
354     void abortMigration(const QString &errorMsg = QString());
355     bool finalizeMigration();
356
357     template<typename T> bool transferMo(MigrationObject moType, T &mo);
358
359     AbstractSqlMigrationWriter *_writer;
360 };
361
362
363 class AbstractSqlMigrationWriter : public AbstractSqlMigrator
364 {
365 public:
366     virtual bool writeMo(const QuasselUserMO &user) = 0;
367     virtual bool writeMo(const IdentityMO &identity) = 0;
368     virtual bool writeMo(const IdentityNickMO &identityNick) = 0;
369     virtual bool writeMo(const NetworkMO &network) = 0;
370     virtual bool writeMo(const BufferMO &buffer) = 0;
371     virtual bool writeMo(const SenderMO &sender) = 0;
372     virtual bool writeMo(const BacklogMO &backlog) = 0;
373     virtual bool writeMo(const IrcServerMO &ircserver) = 0;
374     virtual bool writeMo(const UserSettingMO &userSetting) = 0;
375     virtual bool writeMo(const CoreStateMO &coreState) = 0;
376
377     inline bool migrateFrom(AbstractSqlMigrationReader *reader) { return reader->migrateTo(this); }
378
379     // called after migration process
380     virtual inline bool postProcess() { return true; }
381     friend class AbstractSqlMigrationReader;
382 };