Rework the handling of storage/auth backends and config
[quassel.git] / src / core / abstractsqlstorage.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 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         QString key;
237         bool joined;
238     };
239
240     struct BacklogMO {
241         MsgId messageid;
242         QDateTime time; // has to be in UTC!
243         BufferId bufferid;
244         int type;
245         int flags;
246         int senderid;
247         QString message;
248     };
249
250     struct IrcServerMO {
251         int serverid;
252         UserId userid;
253         NetworkId networkid;
254         QString hostname;
255         int port;
256         QString password;
257         bool ssl;
258         bool sslverify;     /// If true, validate SSL certificates
259         int sslversion;
260         bool useproxy;
261         int proxytype;
262         QString proxyhost;
263         int proxyport;
264         QString proxyuser;
265         QString proxypass;
266     };
267
268     struct UserSettingMO {
269         UserId userid;
270         QString settingname;
271         QByteArray settingvalue;
272     };
273
274     enum MigrationObject {
275         QuasselUser,
276         Sender,
277         Identity,
278         IdentityNick,
279         Network,
280         Buffer,
281         Backlog,
282         IrcServer,
283         UserSetting
284     };
285
286     AbstractSqlMigrator();
287     virtual ~AbstractSqlMigrator() {}
288
289     static QString migrationObject(MigrationObject moType);
290
291 protected:
292     void newQuery(const QString &query, QSqlDatabase db);
293     virtual void resetQuery();
294     virtual bool prepareQuery(MigrationObject mo) = 0;
295     bool exec();
296     inline bool next() { return _query->next(); }
297     inline QVariant value(int index) { return _query->value(index); }
298     inline void bindValue(const QString &placeholder, const QVariant &val) { _query->bindValue(placeholder, val); }
299     inline void bindValue(int pos, const QVariant &val) { _query->bindValue(pos, val); }
300
301     inline QSqlError lastError() { return _query ? _query->lastError() : QSqlError(); }
302     void dumpStatus();
303     inline QString executedQuery() { return _query ? _query->executedQuery() : QString(); }
304     inline QVariantList boundValues();
305
306     virtual bool transaction() = 0;
307     virtual void rollback() = 0;
308     virtual bool commit() = 0;
309
310 private:
311     QSqlQuery *_query;
312 };
313
314
315 class AbstractSqlMigrationReader : public AbstractSqlMigrator
316 {
317 public:
318     AbstractSqlMigrationReader();
319
320     virtual bool readMo(QuasselUserMO &user) = 0;
321     virtual bool readMo(IdentityMO &identity) = 0;
322     virtual bool readMo(IdentityNickMO &identityNick) = 0;
323     virtual bool readMo(NetworkMO &network) = 0;
324     virtual bool readMo(BufferMO &buffer) = 0;
325     virtual bool readMo(SenderMO &sender) = 0;
326     virtual bool readMo(BacklogMO &backlog) = 0;
327     virtual bool readMo(IrcServerMO &ircserver) = 0;
328     virtual bool readMo(UserSettingMO &userSetting) = 0;
329
330     bool migrateTo(AbstractSqlMigrationWriter *writer);
331
332 private:
333     void abortMigration(const QString &errorMsg = QString());
334     bool finalizeMigration();
335
336     template<typename T> bool transferMo(MigrationObject moType, T &mo);
337
338     AbstractSqlMigrationWriter *_writer;
339 };
340
341
342 class AbstractSqlMigrationWriter : public AbstractSqlMigrator
343 {
344 public:
345     virtual bool writeMo(const QuasselUserMO &user) = 0;
346     virtual bool writeMo(const IdentityMO &identity) = 0;
347     virtual bool writeMo(const IdentityNickMO &identityNick) = 0;
348     virtual bool writeMo(const NetworkMO &network) = 0;
349     virtual bool writeMo(const BufferMO &buffer) = 0;
350     virtual bool writeMo(const SenderMO &sender) = 0;
351     virtual bool writeMo(const BacklogMO &backlog) = 0;
352     virtual bool writeMo(const IrcServerMO &ircserver) = 0;
353     virtual bool writeMo(const UserSettingMO &userSetting) = 0;
354
355     inline bool migrateFrom(AbstractSqlMigrationReader *reader) { return reader->migrateTo(this); }
356
357     // called after migration process
358     virtual inline bool postProcess() { return true; }
359     friend class AbstractSqlMigrationReader;
360 };