ssl: Remove fallback code for missing SSL support
[quassel.git] / src / core / sqlitestorage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2020 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 #include "sqlitestorage.h"
22
23 #include <QByteArray>
24 #include <QDataStream>
25 #include <QLatin1String>
26 #include <QVariant>
27
28 #include "network.h"
29 #include "quassel.h"
30
31 int SqliteStorage::_maxRetryCount = 150;
32
33 SqliteStorage::SqliteStorage(QObject* parent)
34     : AbstractSqlStorage(parent)
35 {}
36
37 bool SqliteStorage::isAvailable() const
38 {
39     if (!QSqlDatabase::isDriverAvailable("QSQLITE"))
40         return false;
41     return true;
42 }
43
44 QString SqliteStorage::backendId() const
45 {
46     return QString("SQLite");
47 }
48
49 QString SqliteStorage::displayName() const
50 {
51     // Note: Pre-0.13 clients use the displayName property for backend idenfication
52     // We identify the backend to use for the monolithic core by its displayname.
53     // so only change this string if you _really_ have to and make sure the core
54     // setup for the mono client still works ;)
55     return backendId();
56 }
57
58 QString SqliteStorage::description() const
59 {
60     return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized "
61               "databases that do not require access via network. Use SQLite if your Quassel Core should store its data on the same machine "
62               "it is running on, and if you only expect a few users to use your core.");
63 }
64
65 int SqliteStorage::installedSchemaVersion()
66 {
67     // only used when there is a singlethread (during startup)
68     // so we don't need locking here
69     QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
70     if (query.first())
71         return query.value(0).toInt();
72
73     // maybe it's really old... (schema version 0)
74     query = logDb().exec("SELECT MAX(version) FROM coreinfo");
75     if (query.first())
76         return query.value(0).toInt();
77
78     return AbstractSqlStorage::installedSchemaVersion();
79 }
80
81 bool SqliteStorage::updateSchemaVersion(int newVersion, bool clearUpgradeStep)
82 {
83     // only used when there is a singlethread (during startup)
84     // so we don't need locking here
85
86     QSqlDatabase db = logDb();
87
88     // Atomically update the schema version and clear the upgrade step, if specified
89     // Note: This will need reworked if "updateSchemaVersion" is ever called within a transaction.
90     db.transaction();
91
92     QSqlQuery query(db);
93     query.prepare("UPDATE coreinfo SET value = :version WHERE key = 'schemaversion'");
94     query.bindValue(":version", newVersion);
95     safeExec(query);
96
97     if (!watchQuery(query)) {
98         qCritical() << "SqliteStorage::updateSchemaVersion(int, bool): Updating schema version failed!";
99         db.rollback();
100         return false;
101     }
102
103     if (clearUpgradeStep) {
104         // Try clearing the upgrade step if requested
105         if (!setSchemaVersionUpgradeStep("")) {
106             db.rollback();
107             return false;
108         }
109     }
110
111     // Successful, commit and return true
112     db.commit();
113     return true;
114 }
115
116 bool SqliteStorage::setupSchemaVersion(int version)
117 {
118     // only used when there is a singlethread (during startup)
119     // so we don't need locking here
120     QSqlQuery query(logDb());
121     query.prepare("INSERT INTO coreinfo (key, value) VALUES ('schemaversion', :version)");
122     query.bindValue(":version", version);
123     query.exec();
124
125     bool success = true;
126     if (query.lastError().isValid()) {
127         qCritical() << "SqliteStorage::setupSchemaVersion(int): Updating schema version failed!";
128         success = false;
129     }
130     return success;
131 }
132
133 QString SqliteStorage::schemaVersionUpgradeStep()
134 {
135     // Only used when there is a singlethread (during startup), so we don't need locking here
136     QSqlQuery query(logDb());
137     query.prepare("SELECT value FROM coreinfo WHERE key = 'schemaupgradestep'");
138     safeExec(query);
139     watchQuery(query);
140     if (query.first())
141         return query.value(0).toString();
142
143     // Fall back to the default value
144     return AbstractSqlStorage::schemaVersionUpgradeStep();
145 }
146
147 bool SqliteStorage::setSchemaVersionUpgradeStep(QString upgradeQuery)
148 {
149     // Only used when there is a singlethread (during startup), so we don't need locking here
150
151     // Intentionally do not wrap in a transaction so other functions can include multiple operations
152     QSqlQuery query(logDb());
153     query.prepare("UPDATE coreinfo SET value = :upgradestep WHERE key = 'schemaupgradestep'");
154     query.bindValue(":upgradestep", upgradeQuery);
155     safeExec(query);
156
157     // Don't wrap with watchQuery to avoid an alarming message in the log when the key is missing
158     // Make sure that the query didn't fail, and that some non-zero number of rows were affected
159     bool success = !query.lastError().isValid() && query.numRowsAffected() != 0;
160
161     if (!success) {
162         // The key might not exist (Quassel 0.13.0 and older).  Try inserting it...
163         query = QSqlQuery(logDb());
164         query.prepare("INSERT INTO coreinfo (key, value) VALUES ('schemaupgradestep', :upgradestep)");
165         query.bindValue(":upgradestep", upgradeQuery);
166         safeExec(query);
167
168         if (!watchQuery(query)) {
169             qCritical() << Q_FUNC_INFO << "Setting schema upgrade step failed!";
170             success = false;
171         }
172         else {
173             success = true;
174         }
175     }
176     return success;
177 }
178
179 UserId SqliteStorage::addUser(const QString& user, const QString& password, const QString& authenticator)
180 {
181     QSqlDatabase db = logDb();
182     UserId uid;
183
184     db.transaction();
185     // this scope ensures that the query is freed in sqlite before we call unlock()
186     // this ensures that our thread doesn't hold a internal after unlock is called
187     // (see sqlites doc on implicit locking for details)
188     {
189         QSqlQuery query(db);
190         query.prepare(queryString("insert_quasseluser"));
191         query.bindValue(":username", user);
192         query.bindValue(":password", hashPassword(password));
193         query.bindValue(":hashversion", Storage::HashVersion::Latest);
194         query.bindValue(":authenticator", authenticator);
195         lockForWrite();
196         safeExec(query);
197         if (query.lastError().isValid()
198             && query.lastError().nativeErrorCode() == QLatin1String{"19"}) {  // user already exists - sadly 19 seems to be the general constraint violation error...
199             db.rollback();
200         }
201         else {
202             uid = query.lastInsertId().toInt();
203             db.commit();
204         }
205     }
206     unlock();
207
208     if (uid.isValid())
209         emit userAdded(uid, user);
210     return uid;
211 }
212
213 bool SqliteStorage::updateUser(UserId user, const QString& password)
214 {
215     QSqlDatabase db = logDb();
216     bool success = false;
217
218     db.transaction();
219     {
220         QSqlQuery query(db);
221         query.prepare(queryString("update_userpassword"));
222         query.bindValue(":userid", user.toInt());
223         query.bindValue(":password", hashPassword(password));
224         query.bindValue(":hashversion", Storage::HashVersion::Latest);
225         lockForWrite();
226         safeExec(query);
227         success = query.numRowsAffected() != 0;
228         db.commit();
229     }
230     unlock();
231     return success;
232 }
233
234 void SqliteStorage::renameUser(UserId user, const QString& newName)
235 {
236     QSqlDatabase db = logDb();
237     db.transaction();
238     {
239         QSqlQuery query(db);
240         query.prepare(queryString("update_username"));
241         query.bindValue(":userid", user.toInt());
242         query.bindValue(":username", newName);
243         lockForWrite();
244         safeExec(query);
245         db.commit();
246     }
247     unlock();
248     emit userRenamed(user, newName);
249 }
250
251 UserId SqliteStorage::validateUser(const QString& user, const QString& password)
252 {
253     UserId userId;
254     QString hashedPassword;
255     Storage::HashVersion hashVersion = Storage::HashVersion::Latest;
256
257     {
258         QSqlQuery query(logDb());
259         query.prepare(queryString("select_authuser"));
260         query.bindValue(":username", user);
261
262         lockForRead();
263         safeExec(query);
264
265         if (query.first()) {
266             userId = query.value(0).toInt();
267             hashedPassword = query.value(1).toString();
268             hashVersion = static_cast<Storage::HashVersion>(query.value(2).toInt());
269         }
270     }
271     unlock();
272
273     UserId returnUserId;
274     if (userId != 0 && checkHashedPassword(userId, password, hashedPassword, hashVersion)) {
275         returnUserId = userId;
276     }
277     return returnUserId;
278 }
279
280 UserId SqliteStorage::getUserId(const QString& username)
281 {
282     UserId userId;
283
284     {
285         QSqlQuery query(logDb());
286         query.prepare(queryString("select_userid"));
287         query.bindValue(":username", username);
288
289         lockForRead();
290         safeExec(query);
291
292         if (query.first()) {
293             userId = query.value(0).toInt();
294         }
295     }
296     unlock();
297
298     return userId;
299 }
300
301 QString SqliteStorage::getUserAuthenticator(const UserId userid)
302 {
303     QString authenticator = QString("");
304
305     {
306         QSqlQuery query(logDb());
307         query.prepare(queryString("select_authenticator"));
308         query.bindValue(":userid", userid.toInt());
309
310         lockForRead();
311         safeExec(query);
312
313         if (query.first()) {
314             authenticator = query.value(0).toString();
315         }
316     }
317     unlock();
318
319     return authenticator;
320 }
321
322 UserId SqliteStorage::internalUser()
323 {
324     UserId userId;
325
326     {
327         QSqlQuery query(logDb());
328         query.prepare(queryString("select_internaluser"));
329         lockForRead();
330         safeExec(query);
331
332         if (query.first()) {
333             userId = query.value(0).toInt();
334         }
335     }
336     unlock();
337
338     return userId;
339 }
340
341 void SqliteStorage::delUser(UserId user)
342 {
343     QSqlDatabase db = logDb();
344     db.transaction();
345
346     lockForWrite();
347     {
348         QSqlQuery query(db);
349         query.prepare(queryString("delete_backlog_by_uid"));
350         query.bindValue(":userid", user.toInt());
351         safeExec(query);
352
353         query.prepare(queryString("delete_buffers_by_uid"));
354         query.bindValue(":userid", user.toInt());
355         safeExec(query);
356
357         query.prepare(queryString("delete_networks_by_uid"));
358         query.bindValue(":userid", user.toInt());
359         safeExec(query);
360
361         query.prepare(queryString("delete_quasseluser"));
362         query.bindValue(":userid", user.toInt());
363         safeExec(query);
364         // I hate the lack of foreign keys and on delete cascade... :(
365         db.commit();
366     }
367     unlock();
368
369     emit userRemoved(user);
370 }
371
372 void SqliteStorage::setUserSetting(UserId userId, const QString& settingName, const QVariant& data)
373 {
374     QByteArray rawData;
375     QDataStream out(&rawData, QIODevice::WriteOnly);
376     out.setVersion(QDataStream::Qt_4_2);
377     out << data;
378
379     QSqlDatabase db = logDb();
380     db.transaction();
381     {
382         QSqlQuery query(db);
383         query.prepare(queryString("insert_user_setting"));
384         query.bindValue(":userid", userId.toInt());
385         query.bindValue(":settingname", settingName);
386         query.bindValue(":settingvalue", rawData);
387         lockForWrite();
388         safeExec(query);
389
390         if (query.lastError().isValid()) {
391             QSqlQuery updateQuery(db);
392             updateQuery.prepare(queryString("update_user_setting"));
393             updateQuery.bindValue(":userid", userId.toInt());
394             updateQuery.bindValue(":settingname", settingName);
395             updateQuery.bindValue(":settingvalue", rawData);
396             safeExec(updateQuery);
397         }
398         db.commit();
399     }
400     unlock();
401 }
402
403 QVariant SqliteStorage::getUserSetting(UserId userId, const QString& settingName, const QVariant& defaultData)
404 {
405     QVariant data = defaultData;
406     {
407         QSqlQuery query(logDb());
408         query.prepare(queryString("select_user_setting"));
409         query.bindValue(":userid", userId.toInt());
410         query.bindValue(":settingname", settingName);
411         lockForRead();
412         safeExec(query);
413
414         if (query.first()) {
415             QByteArray rawData = query.value(0).toByteArray();
416             QDataStream in(&rawData, QIODevice::ReadOnly);
417             in.setVersion(QDataStream::Qt_4_2);
418             in >> data;
419         }
420     }
421     unlock();
422     return data;
423 }
424
425 void SqliteStorage::setCoreState(const QVariantList& data)
426 {
427     QByteArray rawData;
428     QDataStream out(&rawData, QIODevice::WriteOnly);
429     out.setVersion(QDataStream::Qt_4_2);
430     out << data;
431
432     QSqlDatabase db = logDb();
433     db.transaction();
434     {
435         QSqlQuery query(db);
436         query.prepare(queryString("insert_core_state"));
437         query.bindValue(":key", "active_sessions");
438         query.bindValue(":value", rawData);
439         lockForWrite();
440         safeExec(query);
441
442         if (query.lastError().isValid()) {
443             QSqlQuery updateQuery(db);
444             updateQuery.prepare(queryString("update_core_state"));
445             updateQuery.bindValue(":key", "active_sessions");
446             updateQuery.bindValue(":value", rawData);
447             safeExec(updateQuery);
448         }
449         db.commit();
450     }
451     unlock();
452 }
453
454 QVariantList SqliteStorage::getCoreState(const QVariantList& defaultData)
455 {
456     QVariantList data;
457     {
458         QSqlQuery query(logDb());
459         query.prepare(queryString("select_core_state"));
460         query.bindValue(":key", "active_sessions");
461         lockForRead();
462         safeExec(query);
463
464         if (query.first()) {
465             QByteArray rawData = query.value(0).toByteArray();
466             QDataStream in(&rawData, QIODevice::ReadOnly);
467             in.setVersion(QDataStream::Qt_4_2);
468             in >> data;
469         }
470         else {
471             data = defaultData;
472         }
473     }
474     unlock();
475     return data;
476 }
477
478 IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity& identity)
479 {
480     IdentityId identityId;
481
482     QSqlDatabase db = logDb();
483     db.transaction();
484
485     {
486         QSqlQuery query(db);
487         query.prepare(queryString("insert_identity"));
488         query.bindValue(":userid", user.toInt());
489         query.bindValue(":identityname", identity.identityName());
490         query.bindValue(":realname", identity.realName());
491         query.bindValue(":awaynick", identity.awayNick());
492         query.bindValue(":awaynickenabled", identity.awayNickEnabled() ? 1 : 0);
493         query.bindValue(":awayreason", identity.awayReason());
494         query.bindValue(":awayreasonenabled", identity.awayReasonEnabled() ? 1 : 0);
495         query.bindValue(":autoawayenabled", identity.awayReasonEnabled() ? 1 : 0);
496         query.bindValue(":autoawaytime", identity.autoAwayTime());
497         query.bindValue(":autoawayreason", identity.autoAwayReason());
498         query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled() ? 1 : 0);
499         query.bindValue(":detachawayenabled", identity.detachAwayEnabled() ? 1 : 0);
500         query.bindValue(":detachawayreason", identity.detachAwayReason());
501         query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled() ? 1 : 0);
502         query.bindValue(":ident", identity.ident());
503         query.bindValue(":kickreason", identity.kickReason());
504         query.bindValue(":partreason", identity.partReason());
505         query.bindValue(":quitreason", identity.quitReason());
506         query.bindValue(":sslcert", identity.sslCert().toPem());
507         query.bindValue(":sslkey", identity.sslKey().toPem());
508
509         lockForWrite();
510         safeExec(query);
511
512         identityId = query.lastInsertId().toInt();
513         if (!identityId.isValid()) {
514             watchQuery(query);
515         }
516         else {
517             QSqlQuery deleteNickQuery(db);
518             deleteNickQuery.prepare(queryString("delete_nicks"));
519             deleteNickQuery.bindValue(":identityid", identityId.toInt());
520             safeExec(deleteNickQuery);
521
522             QSqlQuery insertNickQuery(db);
523             insertNickQuery.prepare(queryString("insert_nick"));
524             foreach (QString nick, identity.nicks()) {
525                 insertNickQuery.bindValue(":identityid", identityId.toInt());
526                 insertNickQuery.bindValue(":nick", nick);
527                 safeExec(insertNickQuery);
528             }
529         }
530         db.commit();
531     }
532     unlock();
533     identity.setId(identityId);
534     return identityId;
535 }
536
537 bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity& identity)
538 {
539     QSqlDatabase db = logDb();
540     bool error = false;
541     db.transaction();
542
543     {
544         QSqlQuery checkQuery(db);
545         checkQuery.prepare(queryString("select_checkidentity"));
546         checkQuery.bindValue(":identityid", identity.id().toInt());
547         checkQuery.bindValue(":userid", user.toInt());
548         lockForRead();
549         safeExec(checkQuery);
550
551         // there should be exactly one identity for the given id and user
552         error = (!checkQuery.first() || checkQuery.value(0).toInt() != 1);
553     }
554     if (error) {
555         unlock();
556         return false;
557     }
558
559     {
560         QSqlQuery query(db);
561         query.prepare(queryString("update_identity"));
562         query.bindValue(":identityname", identity.identityName());
563         query.bindValue(":realname", identity.realName());
564         query.bindValue(":awaynick", identity.awayNick());
565         query.bindValue(":awaynickenabled", identity.awayNickEnabled() ? 1 : 0);
566         query.bindValue(":awayreason", identity.awayReason());
567         query.bindValue(":awayreasonenabled", identity.awayReasonEnabled() ? 1 : 0);
568         query.bindValue(":autoawayenabled", identity.awayReasonEnabled() ? 1 : 0);
569         query.bindValue(":autoawaytime", identity.autoAwayTime());
570         query.bindValue(":autoawayreason", identity.autoAwayReason());
571         query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled() ? 1 : 0);
572         query.bindValue(":detachawayenabled", identity.detachAwayEnabled() ? 1 : 0);
573         query.bindValue(":detachawayreason", identity.detachAwayReason());
574         query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled() ? 1 : 0);
575         query.bindValue(":ident", identity.ident());
576         query.bindValue(":kickreason", identity.kickReason());
577         query.bindValue(":partreason", identity.partReason());
578         query.bindValue(":quitreason", identity.quitReason());
579         query.bindValue(":sslcert", identity.sslCert().toPem());
580         query.bindValue(":sslkey", identity.sslKey().toPem());
581         query.bindValue(":identityid", identity.id().toInt());
582         safeExec(query);
583         watchQuery(query);
584
585         QSqlQuery deleteNickQuery(db);
586         deleteNickQuery.prepare(queryString("delete_nicks"));
587         deleteNickQuery.bindValue(":identityid", identity.id().toInt());
588         safeExec(deleteNickQuery);
589         watchQuery(deleteNickQuery);
590
591         QSqlQuery insertNickQuery(db);
592         insertNickQuery.prepare(queryString("insert_nick"));
593         foreach (QString nick, identity.nicks()) {
594             insertNickQuery.bindValue(":identityid", identity.id().toInt());
595             insertNickQuery.bindValue(":nick", nick);
596             safeExec(insertNickQuery);
597             watchQuery(insertNickQuery);
598         }
599         db.commit();
600     }
601     unlock();
602     return true;
603 }
604
605 void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
606 {
607     QSqlDatabase db = logDb();
608     db.transaction();
609
610     bool error = false;
611     {
612         QSqlQuery checkQuery(db);
613         checkQuery.prepare(queryString("select_checkidentity"));
614         checkQuery.bindValue(":identityid", identityId.toInt());
615         checkQuery.bindValue(":userid", user.toInt());
616         lockForRead();
617         safeExec(checkQuery);
618
619         // there should be exactly one identity for the given id and user
620         error = (!checkQuery.first() || checkQuery.value(0).toInt() != 1);
621     }
622     if (error) {
623         unlock();
624         return;
625     }
626
627     {
628         QSqlQuery deleteNickQuery(db);
629         deleteNickQuery.prepare(queryString("delete_nicks"));
630         deleteNickQuery.bindValue(":identityid", identityId.toInt());
631         safeExec(deleteNickQuery);
632
633         QSqlQuery deleteIdentityQuery(db);
634         deleteIdentityQuery.prepare(queryString("delete_identity"));
635         deleteIdentityQuery.bindValue(":identityid", identityId.toInt());
636         deleteIdentityQuery.bindValue(":userid", user.toInt());
637         safeExec(deleteIdentityQuery);
638         db.commit();
639     }
640     unlock();
641 }
642
643 std::vector<CoreIdentity> SqliteStorage::identities(UserId user)
644 {
645     std::vector<CoreIdentity> identities;
646     QSqlDatabase db = logDb();
647     db.transaction();
648
649     {
650         QSqlQuery query(db);
651         query.prepare(queryString("select_identities"));
652         query.bindValue(":userid", user.toInt());
653
654         QSqlQuery nickQuery(db);
655         nickQuery.prepare(queryString("select_nicks"));
656
657         lockForRead();
658         safeExec(query);
659
660         while (query.next()) {
661             CoreIdentity identity(IdentityId(query.value(0).toInt()));
662
663             identity.setIdentityName(query.value(1).toString());
664             identity.setRealName(query.value(2).toString());
665             identity.setAwayNick(query.value(3).toString());
666             identity.setAwayNickEnabled(!!query.value(4).toInt());
667             identity.setAwayReason(query.value(5).toString());
668             identity.setAwayReasonEnabled(!!query.value(6).toInt());
669             identity.setAutoAwayEnabled(!!query.value(7).toInt());
670             identity.setAutoAwayTime(query.value(8).toInt());
671             identity.setAutoAwayReason(query.value(9).toString());
672             identity.setAutoAwayReasonEnabled(!!query.value(10).toInt());
673             identity.setDetachAwayEnabled(!!query.value(11).toInt());
674             identity.setDetachAwayReason(query.value(12).toString());
675             identity.setDetachAwayReasonEnabled(!!query.value(13).toInt());
676             identity.setIdent(query.value(14).toString());
677             identity.setKickReason(query.value(15).toString());
678             identity.setPartReason(query.value(16).toString());
679             identity.setQuitReason(query.value(17).toString());
680             identity.setSslCert(query.value(18).toByteArray());
681             identity.setSslKey(query.value(19).toByteArray());
682
683             nickQuery.bindValue(":identityid", identity.id().toInt());
684             QList<QString> nicks;
685             safeExec(nickQuery);
686             watchQuery(nickQuery);
687             while (nickQuery.next()) {
688                 nicks << nickQuery.value(0).toString();
689             }
690             identity.setNicks(std::move(nicks));
691             identities.push_back(std::move(identity));
692         }
693         db.commit();
694     }
695     unlock();
696     return identities;
697 }
698
699 NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo& info)
700 {
701     NetworkId networkId;
702
703     QSqlDatabase db = logDb();
704     db.transaction();
705
706     bool error = false;
707     {
708         QSqlQuery query(db);
709         query.prepare(queryString("insert_network"));
710         query.bindValue(":userid", user.toInt());
711         bindNetworkInfo(query, info);
712         lockForWrite();
713         safeExec(query);
714         if (!watchQuery(query)) {
715             db.rollback();
716             error = true;
717         }
718         else {
719             networkId = query.lastInsertId().toInt();
720         }
721     }
722     if (error) {
723         unlock();
724         return {};
725     }
726
727     {
728         QSqlQuery insertServersQuery(db);
729         insertServersQuery.prepare(queryString("insert_server"));
730         foreach (Network::Server server, info.serverList) {
731             insertServersQuery.bindValue(":userid", user.toInt());
732             insertServersQuery.bindValue(":networkid", networkId.toInt());
733             bindServerInfo(insertServersQuery, server);
734             safeExec(insertServersQuery);
735             if (!watchQuery(insertServersQuery)) {
736                 db.rollback();
737                 error = true;
738                 break;
739             }
740         }
741         if (!error)
742             db.commit();
743     }
744     unlock();
745     if (error)
746         return {};
747     else
748         return networkId;
749 }
750
751 void SqliteStorage::bindNetworkInfo(QSqlQuery& query, const NetworkInfo& info)
752 {
753     query.bindValue(":networkname", info.networkName);
754     query.bindValue(":identityid", info.identity.toInt());
755     query.bindValue(":encodingcodec", QString(info.codecForEncoding));
756     query.bindValue(":decodingcodec", QString(info.codecForDecoding));
757     query.bindValue(":servercodec", QString(info.codecForServer));
758     query.bindValue(":userandomserver", info.useRandomServer ? 1 : 0);
759     query.bindValue(":perform", info.perform.join("\n"));
760     query.bindValue(":useautoidentify", info.useAutoIdentify ? 1 : 0);
761     query.bindValue(":autoidentifyservice", info.autoIdentifyService);
762     query.bindValue(":autoidentifypassword", info.autoIdentifyPassword);
763     query.bindValue(":usesasl", info.useSasl ? 1 : 0);
764     query.bindValue(":saslaccount", info.saslAccount);
765     query.bindValue(":saslpassword", info.saslPassword);
766     query.bindValue(":useautoreconnect", info.useAutoReconnect ? 1 : 0);
767     query.bindValue(":autoreconnectinterval", info.autoReconnectInterval);
768     query.bindValue(":autoreconnectretries", info.autoReconnectRetries);
769     query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0);
770     query.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0);
771     // Custom rate limiting
772     query.bindValue(":usecustomessagerate", info.useCustomMessageRate ? 1 : 0);
773     query.bindValue(":messagerateburstsize", info.messageRateBurstSize);
774     query.bindValue(":messageratedelay", info.messageRateDelay);
775     query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate ? 1 : 0);
776     if (info.networkId.isValid())
777         query.bindValue(":networkid", info.networkId.toInt());
778 }
779
780 void SqliteStorage::bindServerInfo(QSqlQuery& query, const Network::Server& server)
781 {
782     query.bindValue(":hostname", server.host);
783     query.bindValue(":port", server.port);
784     query.bindValue(":password", server.password);
785     query.bindValue(":ssl", server.useSsl ? 1 : 0);
786     query.bindValue(":sslversion", server.sslVersion);
787     query.bindValue(":useproxy", server.useProxy ? 1 : 0);
788     query.bindValue(":proxytype", server.proxyType);
789     query.bindValue(":proxyhost", server.proxyHost);
790     query.bindValue(":proxyport", server.proxyPort);
791     query.bindValue(":proxyuser", server.proxyUser);
792     query.bindValue(":proxypass", server.proxyPass);
793     query.bindValue(":sslverify", server.sslVerify ? 1 : 0);
794 }
795
796 bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo& info)
797 {
798     QSqlDatabase db = logDb();
799     bool error = false;
800     db.transaction();
801
802     {
803         QSqlQuery updateQuery(db);
804         updateQuery.prepare(queryString("update_network"));
805         updateQuery.bindValue(":userid", user.toInt());
806         bindNetworkInfo(updateQuery, info);
807
808         lockForWrite();
809         safeExec(updateQuery);
810         if (!watchQuery(updateQuery) || updateQuery.numRowsAffected() != 1) {
811             error = true;
812             db.rollback();
813         }
814     }
815     if (error) {
816         unlock();
817         return false;
818     }
819
820     {
821         QSqlQuery dropServersQuery(db);
822         dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
823         dropServersQuery.bindValue(":networkid", info.networkId.toInt());
824         safeExec(dropServersQuery);
825         if (!watchQuery(dropServersQuery)) {
826             error = true;
827             db.rollback();
828         }
829     }
830     if (error) {
831         unlock();
832         return false;
833     }
834
835     {
836         QSqlQuery insertServersQuery(db);
837         insertServersQuery.prepare(queryString("insert_server"));
838         foreach (Network::Server server, info.serverList) {
839             insertServersQuery.bindValue(":userid", user.toInt());
840             insertServersQuery.bindValue(":networkid", info.networkId.toInt());
841             bindServerInfo(insertServersQuery, server);
842             safeExec(insertServersQuery);
843             if (!watchQuery(insertServersQuery)) {
844                 error = true;
845                 db.rollback();
846                 break;
847             }
848         }
849     }
850
851     db.commit();
852     unlock();
853     return !error;
854 }
855
856 bool SqliteStorage::removeNetwork(UserId user, const NetworkId& networkId)
857 {
858     QSqlDatabase db = logDb();
859     bool error = false;
860     db.transaction();
861
862     {
863         QSqlQuery deleteNetworkQuery(db);
864         deleteNetworkQuery.prepare(queryString("delete_network"));
865         deleteNetworkQuery.bindValue(":networkid", networkId.toInt());
866         deleteNetworkQuery.bindValue(":userid", user.toInt());
867         lockForWrite();
868         safeExec(deleteNetworkQuery);
869         if (!watchQuery(deleteNetworkQuery) || deleteNetworkQuery.numRowsAffected() != 1) {
870             error = true;
871             db.rollback();
872         }
873     }
874     if (error) {
875         unlock();
876         return false;
877     }
878
879     {
880         QSqlQuery deleteBacklogQuery(db);
881         deleteBacklogQuery.prepare(queryString("delete_backlog_for_network"));
882         deleteBacklogQuery.bindValue(":networkid", networkId.toInt());
883         safeExec(deleteBacklogQuery);
884         if (!watchQuery(deleteBacklogQuery)) {
885             db.rollback();
886             error = true;
887         }
888     }
889     if (error) {
890         unlock();
891         return false;
892     }
893
894     {
895         QSqlQuery deleteBuffersQuery(db);
896         deleteBuffersQuery.prepare(queryString("delete_buffers_for_network"));
897         deleteBuffersQuery.bindValue(":networkid", networkId.toInt());
898         safeExec(deleteBuffersQuery);
899         if (!watchQuery(deleteBuffersQuery)) {
900             db.rollback();
901             error = true;
902         }
903     }
904     if (error) {
905         unlock();
906         return false;
907     }
908
909     {
910         QSqlQuery deleteServersQuery(db);
911         deleteServersQuery.prepare(queryString("delete_ircservers_for_network"));
912         deleteServersQuery.bindValue(":networkid", networkId.toInt());
913         safeExec(deleteServersQuery);
914         if (!watchQuery(deleteServersQuery)) {
915             db.rollback();
916             error = true;
917         }
918     }
919     if (error) {
920         unlock();
921         return false;
922     }
923
924     db.commit();
925     unlock();
926     return true;
927 }
928
929 std::vector<NetworkInfo> SqliteStorage::networks(UserId user)
930 {
931     std::vector<NetworkInfo> nets;
932
933     QSqlDatabase db = logDb();
934     db.transaction();
935
936     {
937         QSqlQuery networksQuery(db);
938         networksQuery.prepare(queryString("select_networks_for_user"));
939         networksQuery.bindValue(":userid", user.toInt());
940
941         QSqlQuery serversQuery(db);
942         serversQuery.prepare(queryString("select_servers_for_network"));
943
944         lockForRead();
945         safeExec(networksQuery);
946         if (watchQuery(networksQuery)) {
947             while (networksQuery.next()) {
948                 NetworkInfo net;
949                 net.networkId = networksQuery.value(0).toInt();
950                 net.networkName = networksQuery.value(1).toString();
951                 net.identity = networksQuery.value(2).toInt();
952                 net.codecForServer = networksQuery.value(3).toString().toLatin1();
953                 net.codecForEncoding = networksQuery.value(4).toString().toLatin1();
954                 net.codecForDecoding = networksQuery.value(5).toString().toLatin1();
955                 net.useRandomServer = networksQuery.value(6).toInt() == 1 ? true : false;
956                 net.perform = networksQuery.value(7).toString().split("\n");
957                 net.useAutoIdentify = networksQuery.value(8).toInt() == 1 ? true : false;
958                 net.autoIdentifyService = networksQuery.value(9).toString();
959                 net.autoIdentifyPassword = networksQuery.value(10).toString();
960                 net.useAutoReconnect = networksQuery.value(11).toInt() == 1 ? true : false;
961                 net.autoReconnectInterval = networksQuery.value(12).toUInt();
962                 net.autoReconnectRetries = networksQuery.value(13).toInt();
963                 net.unlimitedReconnectRetries = networksQuery.value(14).toInt() == 1 ? true : false;
964                 net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false;
965                 net.useSasl = networksQuery.value(16).toInt() == 1 ? true : false;
966                 net.saslAccount = networksQuery.value(17).toString();
967                 net.saslPassword = networksQuery.value(18).toString();
968                 // Custom rate limiting
969                 net.useCustomMessageRate = networksQuery.value(19).toInt() == 1 ? true : false;
970                 net.messageRateBurstSize = networksQuery.value(20).toUInt();
971                 net.messageRateDelay = networksQuery.value(21).toUInt();
972                 net.unlimitedMessageRate = networksQuery.value(22).toInt() == 1 ? true : false;
973
974                 serversQuery.bindValue(":networkid", net.networkId.toInt());
975                 safeExec(serversQuery);
976                 if (!watchQuery(serversQuery)) {
977                     nets.clear();
978                     break;
979                 }
980                 else {
981                     Network::ServerList servers;
982                     while (serversQuery.next()) {
983                         Network::Server server;
984                         server.host = serversQuery.value(0).toString();
985                         server.port = serversQuery.value(1).toUInt();
986                         server.password = serversQuery.value(2).toString();
987                         server.useSsl = serversQuery.value(3).toInt() == 1 ? true : false;
988                         server.sslVersion = serversQuery.value(4).toInt();
989                         server.useProxy = serversQuery.value(5).toInt() == 1 ? true : false;
990                         server.proxyType = serversQuery.value(6).toInt();
991                         server.proxyHost = serversQuery.value(7).toString();
992                         server.proxyPort = serversQuery.value(8).toUInt();
993                         server.proxyUser = serversQuery.value(9).toString();
994                         server.proxyPass = serversQuery.value(10).toString();
995                         server.sslVerify = serversQuery.value(11).toInt() == 1 ? true : false;
996                         servers << server;
997                     }
998                     net.serverList = servers;
999                     nets.push_back(std::move(net));
1000                 }
1001             }
1002         }
1003     }
1004     db.commit();
1005     unlock();
1006     return nets;
1007 }
1008
1009 std::vector<NetworkId> SqliteStorage::connectedNetworks(UserId user)
1010 {
1011     std::vector<NetworkId> connectedNets;
1012
1013     QSqlDatabase db = logDb();
1014     db.transaction();
1015
1016     {
1017         QSqlQuery query(db);
1018         query.prepare(queryString("select_connected_networks"));
1019         query.bindValue(":userid", user.toInt());
1020         lockForRead();
1021         safeExec(query);
1022         watchQuery(query);
1023
1024         while (query.next()) {
1025             connectedNets.emplace_back(query.value(0).toInt());
1026         }
1027         db.commit();
1028     }
1029     unlock();
1030     return connectedNets;
1031 }
1032
1033 void SqliteStorage::setNetworkConnected(UserId user, const NetworkId& networkId, bool isConnected)
1034 {
1035     QSqlDatabase db = logDb();
1036     db.transaction();
1037
1038     {
1039         QSqlQuery query(db);
1040         query.prepare(queryString("update_network_connected"));
1041         query.bindValue(":userid", user.toInt());
1042         query.bindValue(":networkid", networkId.toInt());
1043         query.bindValue(":connected", isConnected ? 1 : 0);
1044
1045         lockForWrite();
1046         safeExec(query);
1047         watchQuery(query);
1048         db.commit();
1049     }
1050     unlock();
1051 }
1052
1053 QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId& networkId)
1054 {
1055     QHash<QString, QString> persistentChans;
1056
1057     QSqlDatabase db = logDb();
1058     db.transaction();
1059     {
1060         QSqlQuery query(db);
1061         query.prepare(queryString("select_persistent_channels"));
1062         query.bindValue(":userid", user.toInt());
1063         query.bindValue(":networkid", networkId.toInt());
1064
1065         lockForRead();
1066         safeExec(query);
1067         watchQuery(query);
1068         while (query.next()) {
1069             persistentChans[query.value(0).toString()] = query.value(1).toString();
1070         }
1071     }
1072     unlock();
1073     return persistentChans;
1074 }
1075
1076 void SqliteStorage::setChannelPersistent(UserId user, const NetworkId& networkId, const QString& channel, bool isJoined)
1077 {
1078     QSqlDatabase db = logDb();
1079     db.transaction();
1080
1081     {
1082         QSqlQuery query(db);
1083         query.prepare(queryString("update_buffer_persistent_channel"));
1084         query.bindValue(":userid", user.toInt());
1085         query.bindValue(":networkid", networkId.toInt());
1086         query.bindValue(":buffercname", channel.toLower());
1087         query.bindValue(":joined", isJoined ? 1 : 0);
1088
1089         lockForWrite();
1090         safeExec(query);
1091         watchQuery(query);
1092         db.commit();
1093     }
1094     unlock();
1095 }
1096
1097 void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId& networkId, const QString& channel, const QString& key)
1098 {
1099     QSqlDatabase db = logDb();
1100     db.transaction();
1101
1102     {
1103         QSqlQuery query(db);
1104         query.prepare(queryString("update_buffer_set_channel_key"));
1105         query.bindValue(":userid", user.toInt());
1106         query.bindValue(":networkid", networkId.toInt());
1107         query.bindValue(":buffercname", channel.toLower());
1108         query.bindValue(":key", key);
1109
1110         lockForWrite();
1111         safeExec(query);
1112         watchQuery(query);
1113         db.commit();
1114     }
1115     unlock();
1116 }
1117
1118 QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
1119 {
1120     QSqlDatabase db = logDb();
1121     db.transaction();
1122
1123     QString awayMsg;
1124     {
1125         QSqlQuery query(db);
1126         query.prepare(queryString("select_network_awaymsg"));
1127         query.bindValue(":userid", user.toInt());
1128         query.bindValue(":networkid", networkId.toInt());
1129
1130         lockForRead();
1131         safeExec(query);
1132         watchQuery(query);
1133         if (query.first())
1134             awayMsg = query.value(0).toString();
1135         db.commit();
1136     }
1137     unlock();
1138
1139     return awayMsg;
1140 }
1141
1142 void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QString& awayMsg)
1143 {
1144     QSqlDatabase db = logDb();
1145     db.transaction();
1146
1147     {
1148         QSqlQuery query(db);
1149         query.prepare(queryString("update_network_set_awaymsg"));
1150         query.bindValue(":userid", user.toInt());
1151         query.bindValue(":networkid", networkId.toInt());
1152         query.bindValue(":awaymsg", awayMsg);
1153
1154         lockForWrite();
1155         safeExec(query);
1156         watchQuery(query);
1157         db.commit();
1158     }
1159     unlock();
1160 }
1161
1162 QString SqliteStorage::userModes(UserId user, NetworkId networkId)
1163 {
1164     QSqlDatabase db = logDb();
1165     db.transaction();
1166
1167     QString modes;
1168     {
1169         QSqlQuery query(db);
1170         query.prepare(queryString("select_network_usermode"));
1171         query.bindValue(":userid", user.toInt());
1172         query.bindValue(":networkid", networkId.toInt());
1173
1174         lockForRead();
1175         safeExec(query);
1176         watchQuery(query);
1177         if (query.first())
1178             modes = query.value(0).toString();
1179         db.commit();
1180     }
1181     unlock();
1182
1183     return modes;
1184 }
1185
1186 void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString& userModes)
1187 {
1188     QSqlDatabase db = logDb();
1189     db.transaction();
1190
1191     {
1192         QSqlQuery query(db);
1193         query.prepare(queryString("update_network_set_usermode"));
1194         query.bindValue(":userid", user.toInt());
1195         query.bindValue(":networkid", networkId.toInt());
1196         query.bindValue(":usermode", userModes);
1197
1198         lockForWrite();
1199         safeExec(query);
1200         watchQuery(query);
1201         db.commit();
1202     }
1203     unlock();
1204 }
1205
1206 BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId& networkId, BufferInfo::Type type, const QString& buffer, bool create)
1207 {
1208     QSqlDatabase db = logDb();
1209     db.transaction();
1210
1211     BufferInfo bufferInfo;
1212     {
1213         QSqlQuery query(db);
1214         query.prepare(queryString("select_bufferByName"));
1215         query.bindValue(":networkid", networkId.toInt());
1216         query.bindValue(":userid", user.toInt());
1217         query.bindValue(":buffercname", buffer.toLower());
1218
1219         lockForRead();
1220         safeExec(query);
1221
1222         if (query.first()) {
1223             bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
1224             if (query.next()) {
1225                 qCritical() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
1226                 qCritical() << "         Query:" << query.lastQuery();
1227                 qCritical() << "  bound Values:";
1228                 QList<QVariant> list = query.boundValues().values();
1229                 for (int i = 0; i < list.size(); ++i)
1230                     qCritical() << i << ":" << list.at(i).toString().toLatin1().data();
1231                 Q_ASSERT(false);
1232             }
1233         }
1234         else if (create) {
1235             // let's create the buffer
1236             QSqlQuery createQuery(db);
1237             createQuery.prepare(queryString("insert_buffer"));
1238             createQuery.bindValue(":userid", user.toInt());
1239             createQuery.bindValue(":networkid", networkId.toInt());
1240             createQuery.bindValue(":buffertype", (int)type);
1241             createQuery.bindValue(":buffername", buffer);
1242             createQuery.bindValue(":buffercname", buffer.toLower());
1243             createQuery.bindValue(":joined", type & BufferInfo::ChannelBuffer ? 1 : 0);
1244
1245             unlock();
1246             lockForWrite();
1247             safeExec(createQuery);
1248             watchQuery(createQuery);
1249             bufferInfo = BufferInfo(createQuery.lastInsertId().toInt(), networkId, type, 0, buffer);
1250         }
1251     }
1252     db.commit();
1253     unlock();
1254     return bufferInfo;
1255 }
1256
1257 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId& bufferId)
1258 {
1259     QSqlDatabase db = logDb();
1260     db.transaction();
1261
1262     BufferInfo bufferInfo;
1263     {
1264         QSqlQuery query(db);
1265         query.prepare(queryString("select_buffer_by_id"));
1266         query.bindValue(":userid", user.toInt());
1267         query.bindValue(":bufferid", bufferId.toInt());
1268
1269         lockForRead();
1270         safeExec(query);
1271
1272         if (watchQuery(query) && query.first()) {
1273             bufferInfo = BufferInfo(query.value(0).toInt(),
1274                                     query.value(1).toInt(),
1275                                     (BufferInfo::Type)query.value(2).toInt(),
1276                                     0,
1277                                     query.value(4).toString());
1278             Q_ASSERT(!query.next());
1279         }
1280         db.commit();
1281     }
1282     unlock();
1283     return bufferInfo;
1284 }
1285
1286 std::vector<BufferInfo> SqliteStorage::requestBuffers(UserId user)
1287 {
1288     std::vector<BufferInfo> bufferlist;
1289
1290     QSqlDatabase db = logDb();
1291     db.transaction();
1292
1293     {
1294         QSqlQuery query(db);
1295         query.prepare(queryString("select_buffers"));
1296         query.bindValue(":userid", user.toInt());
1297
1298         lockForRead();
1299         safeExec(query);
1300         watchQuery(query);
1301         while (query.next()) {
1302             bufferlist.emplace_back(query.value(0).toInt(),
1303                                     query.value(1).toInt(),
1304                                     (BufferInfo::Type)query.value(2).toInt(),
1305                                     query.value(3).toInt(),
1306                                     query.value(4).toString());
1307         }
1308         db.commit();
1309     }
1310     unlock();
1311
1312     return bufferlist;
1313 }
1314
1315 std::vector<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
1316 {
1317     std::vector<BufferId> bufferList;
1318
1319     QSqlDatabase db = logDb();
1320     db.transaction();
1321
1322     {
1323         QSqlQuery query(db);
1324         query.prepare(queryString("select_buffers_for_network"));
1325         query.bindValue(":networkid", networkId.toInt());
1326         query.bindValue(":userid", user.toInt());
1327
1328         lockForRead();
1329         safeExec(query);
1330         watchQuery(query);
1331         while (query.next()) {
1332             bufferList.emplace_back(query.value(0).toInt());
1333         }
1334         db.commit();
1335     }
1336     unlock();
1337
1338     return bufferList;
1339 }
1340
1341 bool SqliteStorage::removeBuffer(const UserId& user, const BufferId& bufferId)
1342 {
1343     QSqlDatabase db = logDb();
1344     db.transaction();
1345
1346     bool error = false;
1347     {
1348         QSqlQuery delBufferQuery(db);
1349         delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1350         delBufferQuery.bindValue(":bufferid", bufferId.toInt());
1351         delBufferQuery.bindValue(":userid", user.toInt());
1352
1353         lockForWrite();
1354         safeExec(delBufferQuery);
1355
1356         error = (!watchQuery(delBufferQuery) || delBufferQuery.numRowsAffected() != 1);
1357     }
1358
1359     if (error) {
1360         db.rollback();
1361         unlock();
1362         return false;
1363     }
1364
1365     {
1366         QSqlQuery delBacklogQuery(db);
1367         delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
1368         delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
1369
1370         safeExec(delBacklogQuery);
1371         error = !watchQuery(delBacklogQuery);
1372     }
1373
1374     if (error) {
1375         db.rollback();
1376     }
1377     else {
1378         db.commit();
1379     }
1380     unlock();
1381     return !error;
1382 }
1383
1384 bool SqliteStorage::renameBuffer(const UserId& user, const BufferId& bufferId, const QString& newName)
1385 {
1386     QSqlDatabase db = logDb();
1387     db.transaction();
1388
1389     bool error = false;
1390     {
1391         QSqlQuery query(db);
1392         query.prepare(queryString("update_buffer_name"));
1393         query.bindValue(":buffername", newName);
1394         query.bindValue(":buffercname", newName.toLower());
1395         query.bindValue(":bufferid", bufferId.toInt());
1396         query.bindValue(":userid", user.toInt());
1397
1398         lockForWrite();
1399         safeExec(query);
1400
1401         error = query.lastError().isValid();
1402         // unexepcted error occured (19 == constraint violation)
1403         if (error && query.lastError().nativeErrorCode() != QLatin1String{"19"}) {
1404             watchQuery(query);
1405         }
1406         else {
1407             error |= (query.numRowsAffected() != 1);
1408         }
1409     }
1410     if (error) {
1411         db.rollback();
1412     }
1413     else {
1414         db.commit();
1415     }
1416     unlock();
1417     return !error;
1418 }
1419
1420 bool SqliteStorage::mergeBuffersPermanently(const UserId& user, const BufferId& bufferId1, const BufferId& bufferId2)
1421 {
1422     QSqlDatabase db = logDb();
1423     db.transaction();
1424
1425     bool error = false;
1426     {
1427         QSqlQuery checkQuery(db);
1428         checkQuery.prepare(queryString("select_buffers_for_merge"));
1429         checkQuery.bindValue(":oldbufferid", bufferId2.toInt());
1430         checkQuery.bindValue(":newbufferid", bufferId1.toInt());
1431         checkQuery.bindValue(":userid", user.toInt());
1432
1433         lockForRead();
1434         safeExec(checkQuery);
1435         error = (!checkQuery.first() || checkQuery.value(0).toInt() != 2);
1436     }
1437     if (error) {
1438         db.rollback();
1439         unlock();
1440         return false;
1441     }
1442
1443     {
1444         QSqlQuery query(db);
1445         query.prepare(queryString("update_backlog_bufferid"));
1446         query.bindValue(":oldbufferid", bufferId2.toInt());
1447         query.bindValue(":newbufferid", bufferId1.toInt());
1448         safeExec(query);
1449         error = !watchQuery(query);
1450     }
1451     if (error) {
1452         db.rollback();
1453         unlock();
1454         return false;
1455     }
1456
1457     {
1458         QSqlQuery delBufferQuery(db);
1459         delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1460         delBufferQuery.bindValue(":bufferid", bufferId2.toInt());
1461         delBufferQuery.bindValue(":userid", user.toInt());
1462         safeExec(delBufferQuery);
1463         error = !watchQuery(delBufferQuery);
1464     }
1465
1466     if (error) {
1467         db.rollback();
1468     }
1469     else {
1470         db.commit();
1471     }
1472     unlock();
1473     return !error;
1474 }
1475
1476 QHash<BufferId, MsgId> SqliteStorage::bufferLastMsgIds(UserId user)
1477 {
1478     QHash<BufferId, MsgId> lastMsgHash;
1479
1480     QSqlDatabase db = logDb();
1481     db.transaction();
1482
1483     bool error = false;
1484     {
1485         QSqlQuery query(db);
1486         query.prepare(queryString("select_buffer_last_messages"));
1487         query.bindValue(":userid", user.toInt());
1488
1489         lockForRead();
1490         safeExec(query);
1491         error = !watchQuery(query);
1492         if (!error) {
1493             while (query.next()) {
1494                 lastMsgHash[query.value(0).toInt()] = query.value(1).toLongLong();
1495             }
1496         }
1497     }
1498
1499     db.commit();
1500     unlock();
1501     return lastMsgHash;
1502 }
1503
1504 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
1505 {
1506     QSqlDatabase db = logDb();
1507     db.transaction();
1508
1509     {
1510         QSqlQuery query(db);
1511         query.prepare(queryString("update_buffer_lastseen"));
1512         query.bindValue(":userid", user.toInt());
1513         query.bindValue(":bufferid", bufferId.toInt());
1514         query.bindValue(":lastseenmsgid", msgId.toQint64());
1515
1516         lockForWrite();
1517         safeExec(query);
1518         watchQuery(query);
1519     }
1520     db.commit();
1521     unlock();
1522 }
1523
1524 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
1525 {
1526     QHash<BufferId, MsgId> lastSeenHash;
1527
1528     QSqlDatabase db = logDb();
1529     db.transaction();
1530
1531     bool error = false;
1532     {
1533         QSqlQuery query(db);
1534         query.prepare(queryString("select_buffer_lastseen_messages"));
1535         query.bindValue(":userid", user.toInt());
1536
1537         lockForRead();
1538         safeExec(query);
1539         error = !watchQuery(query);
1540         if (!error) {
1541             while (query.next()) {
1542                 lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong();
1543             }
1544         }
1545     }
1546
1547     db.commit();
1548     unlock();
1549     return lastSeenHash;
1550 }
1551
1552 void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
1553 {
1554     QSqlDatabase db = logDb();
1555     db.transaction();
1556
1557     {
1558         QSqlQuery query(db);
1559         query.prepare(queryString("update_buffer_markerlinemsgid"));
1560         query.bindValue(":userid", user.toInt());
1561         query.bindValue(":bufferid", bufferId.toInt());
1562         query.bindValue(":markerlinemsgid", msgId.toQint64());
1563
1564         lockForWrite();
1565         safeExec(query);
1566         watchQuery(query);
1567     }
1568     db.commit();
1569     unlock();
1570 }
1571
1572 QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
1573 {
1574     QHash<BufferId, MsgId> markerLineHash;
1575
1576     QSqlDatabase db = logDb();
1577     db.transaction();
1578
1579     bool error = false;
1580     {
1581         QSqlQuery query(db);
1582         query.prepare(queryString("select_buffer_markerlinemsgids"));
1583         query.bindValue(":userid", user.toInt());
1584
1585         lockForRead();
1586         safeExec(query);
1587         error = !watchQuery(query);
1588         if (!error) {
1589             while (query.next()) {
1590                 markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong();
1591             }
1592         }
1593     }
1594
1595     db.commit();
1596     unlock();
1597     return markerLineHash;
1598 }
1599
1600 void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::Types bufferActivity)
1601 {
1602     QSqlDatabase db = logDb();
1603     db.transaction();
1604
1605     {
1606         QSqlQuery query(db);
1607         query.prepare(queryString("update_buffer_bufferactivity"));
1608         query.bindValue(":userid", user.toInt());
1609         query.bindValue(":bufferid", bufferId.toInt());
1610         query.bindValue(":bufferactivity", (int)bufferActivity);
1611
1612         lockForWrite();
1613         safeExec(query);
1614         watchQuery(query);
1615     }
1616     db.commit();
1617     unlock();
1618 }
1619
1620 QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
1621 {
1622     QHash<BufferId, Message::Types> bufferActivityHash;
1623
1624     QSqlDatabase db = logDb();
1625     db.transaction();
1626
1627     bool error = false;
1628     {
1629         QSqlQuery query(db);
1630         query.prepare(queryString("select_buffer_bufferactivities"));
1631         query.bindValue(":userid", user.toInt());
1632
1633         lockForRead();
1634         safeExec(query);
1635         error = !watchQuery(query);
1636         if (!error) {
1637             while (query.next()) {
1638                 bufferActivityHash[query.value(0).toInt()] = Message::Types(query.value(1).toInt());
1639             }
1640         }
1641     }
1642
1643     db.commit();
1644     unlock();
1645     return bufferActivityHash;
1646 }
1647
1648 Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId)
1649 {
1650     QSqlDatabase db = logDb();
1651     db.transaction();
1652
1653     Message::Types result = Message::Types(nullptr);
1654     {
1655         QSqlQuery query(db);
1656         query.prepare(queryString("select_buffer_bufferactivity"));
1657         query.bindValue(":bufferid", bufferId.toInt());
1658         query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
1659
1660         lockForRead();
1661         safeExec(query);
1662         if (query.first())
1663             result = Message::Types(query.value(0).toInt());
1664     }
1665
1666     db.commit();
1667     unlock();
1668     return result;
1669 }
1670
1671 QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkId& networkId)
1672 {
1673     QHash<QString, QByteArray> bufferCiphers;
1674
1675     QSqlDatabase db = logDb();
1676     db.transaction();
1677     {
1678         QSqlQuery query(db);
1679         query.prepare(queryString("select_buffer_ciphers"));
1680         query.bindValue(":userid", user.toInt());
1681         query.bindValue(":networkid", networkId.toInt());
1682
1683         lockForRead();
1684         safeExec(query);
1685         watchQuery(query);
1686         while (query.next()) {
1687             bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8());
1688         }
1689     }
1690     unlock();
1691     return bufferCiphers;
1692 }
1693
1694 void SqliteStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher)
1695 {
1696     QSqlDatabase db = logDb();
1697     db.transaction();
1698
1699     {
1700         QSqlQuery query(db);
1701         query.prepare(queryString("update_buffer_cipher"));
1702         query.bindValue(":userid", user.toInt());
1703         query.bindValue(":networkid", networkId.toInt());
1704         query.bindValue(":buffercname", bufferName.toLower());
1705         query.bindValue(":cipher", QString(cipher.toHex()));
1706
1707         lockForWrite();
1708         safeExec(query);
1709         watchQuery(query);
1710         db.commit();
1711     }
1712     unlock();
1713 }
1714
1715 void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count)
1716 {
1717     QSqlDatabase db = logDb();
1718     db.transaction();
1719
1720     {
1721         QSqlQuery query(db);
1722         query.prepare(queryString("update_buffer_highlightcount"));
1723         query.bindValue(":userid", user.toInt());
1724         query.bindValue(":bufferid", bufferId.toInt());
1725         query.bindValue(":highlightcount", count);
1726
1727         lockForWrite();
1728         safeExec(query);
1729         watchQuery(query);
1730     }
1731     db.commit();
1732     unlock();
1733 }
1734
1735 QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
1736 {
1737     QHash<BufferId, int> highlightCountHash;
1738
1739     QSqlDatabase db = logDb();
1740     db.transaction();
1741
1742     bool error = false;
1743     {
1744         QSqlQuery query(db);
1745         query.prepare(queryString("select_buffer_highlightcounts"));
1746         query.bindValue(":userid", user.toInt());
1747
1748         lockForRead();
1749         safeExec(query);
1750         error = !watchQuery(query);
1751         if (!error) {
1752             while (query.next()) {
1753                 highlightCountHash[query.value(0).toInt()] = query.value(1).toInt();
1754             }
1755         }
1756     }
1757
1758     db.commit();
1759     unlock();
1760     return highlightCountHash;
1761 }
1762
1763 int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
1764 {
1765     QSqlDatabase db = logDb();
1766     db.transaction();
1767
1768     int result = 0;
1769     {
1770         QSqlQuery query(db);
1771         query.prepare(queryString("select_buffer_highlightcount"));
1772         query.bindValue(":bufferid", bufferId.toInt());
1773         query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
1774
1775         lockForRead();
1776         safeExec(query);
1777         if (query.first())
1778             result = query.value(0).toInt();
1779     }
1780
1781     db.commit();
1782     unlock();
1783     return result;
1784 }
1785
1786 bool SqliteStorage::logMessage(Message& msg)
1787 {
1788     QSqlDatabase db = logDb();
1789     db.transaction();
1790
1791     bool error = false;
1792     {
1793         QSqlQuery logMessageQuery(db);
1794         logMessageQuery.prepare(queryString("insert_message"));
1795         // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1796         // seconds.  This nets us more precision as well as simplifying 64-bit time.
1797         logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
1798         logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1799         logMessageQuery.bindValue(":type", msg.type());
1800         logMessageQuery.bindValue(":flags", (int)msg.flags());
1801         logMessageQuery.bindValue(":sender", msg.sender());
1802         logMessageQuery.bindValue(":realname", msg.realName());
1803         logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
1804         logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
1805         logMessageQuery.bindValue(":message", msg.contents());
1806
1807         lockForWrite();
1808         safeExec(logMessageQuery);
1809
1810         if (logMessageQuery.lastError().isValid()) {
1811             // constraint violation - must be NOT NULL constraint - probably the sender is missing...
1812             if (logMessageQuery.lastError().nativeErrorCode() == QLatin1String{"19"}) {
1813                 QSqlQuery addSenderQuery(db);
1814                 addSenderQuery.prepare(queryString("insert_sender"));
1815                 addSenderQuery.bindValue(":sender", msg.sender());
1816                 addSenderQuery.bindValue(":realname", msg.realName());
1817                 addSenderQuery.bindValue(":avatarurl", msg.avatarUrl());
1818                 safeExec(addSenderQuery);
1819                 safeExec(logMessageQuery);
1820                 error = !watchQuery(logMessageQuery);
1821             }
1822             else {
1823                 watchQuery(logMessageQuery);
1824             }
1825         }
1826         if (!error) {
1827             MsgId msgId = logMessageQuery.lastInsertId().toLongLong();
1828             if (msgId.isValid()) {
1829                 msg.setMsgId(msgId);
1830             }
1831             else {
1832                 error = true;
1833             }
1834         }
1835     }
1836
1837     if (error) {
1838         db.rollback();
1839     }
1840     else {
1841         db.commit();
1842     }
1843
1844     unlock();
1845     return !error;
1846 }
1847
1848 bool SqliteStorage::logMessages(MessageList& msgs)
1849 {
1850     QSqlDatabase db = logDb();
1851     db.transaction();
1852
1853     {
1854         QSet<SenderData> senders;
1855         QSqlQuery addSenderQuery(db);
1856         addSenderQuery.prepare(queryString("insert_sender"));
1857         lockForWrite();
1858         for (int i = 0; i < msgs.count(); i++) {
1859             auto& msg = msgs.at(i);
1860             SenderData sender = {msg.sender(), msg.realName(), msg.avatarUrl()};
1861             if (senders.contains(sender))
1862                 continue;
1863             senders << sender;
1864
1865             addSenderQuery.bindValue(":sender", sender.sender);
1866             addSenderQuery.bindValue(":realname", sender.realname);
1867             addSenderQuery.bindValue(":avatarurl", sender.avatarurl);
1868             safeExec(addSenderQuery);
1869         }
1870     }
1871
1872     bool error = false;
1873     {
1874         QSqlQuery logMessageQuery(db);
1875         logMessageQuery.prepare(queryString("insert_message"));
1876         for (int i = 0; i < msgs.count(); i++) {
1877             Message& msg = msgs[i];
1878             // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1879             // seconds.  This nets us more precision as well as simplifying 64-bit time.
1880             logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
1881             logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1882             logMessageQuery.bindValue(":type", msg.type());
1883             logMessageQuery.bindValue(":flags", (int)msg.flags());
1884             logMessageQuery.bindValue(":sender", msg.sender());
1885             logMessageQuery.bindValue(":realname", msg.realName());
1886             logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
1887             logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
1888             logMessageQuery.bindValue(":message", msg.contents());
1889
1890             safeExec(logMessageQuery);
1891             if (!watchQuery(logMessageQuery)) {
1892                 error = true;
1893                 break;
1894             }
1895             else {
1896                 msg.setMsgId(logMessageQuery.lastInsertId().toLongLong());
1897             }
1898         }
1899     }
1900
1901     if (error) {
1902         db.rollback();
1903         unlock();
1904         // we had a rollback in the db so we need to reset all msgIds
1905         for (int i = 0; i < msgs.count(); i++) {
1906             msgs[i].setMsgId(MsgId());
1907         }
1908     }
1909     else {
1910         db.commit();
1911         unlock();
1912     }
1913     return !error;
1914 }
1915
1916 std::vector<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
1917 {
1918     std::vector<Message> messagelist;
1919
1920     QSqlDatabase db = logDb();
1921     db.transaction();
1922
1923     bool error = false;
1924     BufferInfo bufferInfo;
1925     {
1926         // code duplication from getBufferInfo:
1927         // this is due to the impossibility of nesting transactions and recursive locking
1928         QSqlQuery bufferInfoQuery(db);
1929         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
1930         bufferInfoQuery.bindValue(":userid", user.toInt());
1931         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
1932
1933         lockForRead();
1934         safeExec(bufferInfoQuery);
1935         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
1936         if (!error) {
1937             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
1938                                     bufferInfoQuery.value(1).toInt(),
1939                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
1940                                     0,
1941                                     bufferInfoQuery.value(4).toString());
1942             error = !bufferInfo.isValid();
1943         }
1944     }
1945     if (error) {
1946         db.rollback();
1947         unlock();
1948         return messagelist;
1949     }
1950
1951     {
1952         QSqlQuery query(db);
1953         if (last == -1 && first == -1) {
1954             query.prepare(queryString("select_messagesNewestK"));
1955         }
1956         else if (last == -1) {
1957             query.prepare(queryString("select_messagesNewerThan"));
1958             query.bindValue(":firstmsg", first.toQint64());
1959         }
1960         else {
1961             query.prepare(queryString("select_messagesRange"));
1962             query.bindValue(":lastmsg", last.toQint64());
1963             query.bindValue(":firstmsg", first.toQint64());
1964         }
1965         query.bindValue(":bufferid", bufferId.toInt());
1966         query.bindValue(":limit", limit);
1967
1968         safeExec(query);
1969         watchQuery(query);
1970
1971         while (query.next()) {
1972             Message msg(
1973                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1974                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
1975                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
1976                 bufferInfo,
1977                 (Message::Type)query.value(2).toInt(),
1978                 query.value(8).toString(),
1979                 query.value(4).toString(),
1980                 query.value(5).toString(),
1981                 query.value(6).toString(),
1982                 query.value(7).toString(),
1983                 (Message::Flags)query.value(3).toInt());
1984             msg.setMsgId(query.value(0).toLongLong());
1985             messagelist.push_back(std::move(msg));
1986         }
1987     }
1988     db.commit();
1989     unlock();
1990
1991     return messagelist;
1992 }
1993
1994 std::vector<Message> SqliteStorage::requestMsgsFiltered(
1995     UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
1996 {
1997     std::vector<Message> messagelist;
1998
1999     QSqlDatabase db = logDb();
2000     db.transaction();
2001
2002     bool error = false;
2003     BufferInfo bufferInfo;
2004     {
2005         // code dupication from getBufferInfo:
2006         // this is due to the impossibility of nesting transactions and recursive locking
2007         QSqlQuery bufferInfoQuery(db);
2008         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
2009         bufferInfoQuery.bindValue(":userid", user.toInt());
2010         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
2011
2012         lockForRead();
2013         safeExec(bufferInfoQuery);
2014         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
2015         if (!error) {
2016             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2017                                     bufferInfoQuery.value(1).toInt(),
2018                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2019                                     0,
2020                                     bufferInfoQuery.value(4).toString());
2021             error = !bufferInfo.isValid();
2022         }
2023     }
2024     if (error) {
2025         db.rollback();
2026         unlock();
2027         return messagelist;
2028     }
2029
2030     {
2031         QSqlQuery query(db);
2032         if (last == -1 && first == -1) {
2033             query.prepare(queryString("select_messagesNewestK_filtered"));
2034         }
2035         else if (last == -1) {
2036             query.prepare(queryString("select_messagesNewerThan_filtered"));
2037             query.bindValue(":firstmsg", first.toQint64());
2038         }
2039         else {
2040             query.prepare(queryString("select_messagesRange_filtered"));
2041             query.bindValue(":lastmsg", last.toQint64());
2042             query.bindValue(":firstmsg", first.toQint64());
2043         }
2044         query.bindValue(":bufferid", bufferId.toInt());
2045         query.bindValue(":limit", limit);
2046         int typeRaw = type;
2047         query.bindValue(":type", typeRaw);
2048         int flagsRaw = flags;
2049         query.bindValue(":flags", flagsRaw);
2050
2051         safeExec(query);
2052         watchQuery(query);
2053
2054         while (query.next()) {
2055             Message msg(
2056                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2057                 // instead of seconds.  This nets us more precision as well as simplifying
2058                 // 64-bit time.
2059                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
2060                 bufferInfo,
2061                 (Message::Type)query.value(2).toInt(),
2062                 query.value(8).toString(),
2063                 query.value(4).toString(),
2064                 query.value(5).toString(),
2065                 query.value(6).toString(),
2066                 query.value(7).toString(),
2067                 Message::Flags{query.value(3).toInt()});
2068             msg.setMsgId(query.value(0).toLongLong());
2069             messagelist.push_back(std::move(msg));
2070         }
2071     }
2072     db.commit();
2073     unlock();
2074
2075     return messagelist;
2076 }
2077
2078 std::vector<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
2079 {
2080     std::vector<Message> messagelist;
2081
2082     QSqlDatabase db = logDb();
2083     db.transaction();
2084
2085     QHash<BufferId, BufferInfo> bufferInfoHash;
2086     {
2087         QSqlQuery bufferInfoQuery(db);
2088         bufferInfoQuery.prepare(queryString("select_buffers"));
2089         bufferInfoQuery.bindValue(":userid", user.toInt());
2090
2091         lockForRead();
2092         safeExec(bufferInfoQuery);
2093         watchQuery(bufferInfoQuery);
2094         while (bufferInfoQuery.next()) {
2095             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2096                                                bufferInfoQuery.value(1).toInt(),
2097                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2098                                                bufferInfoQuery.value(3).toInt(),
2099                                                bufferInfoQuery.value(4).toString());
2100             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2101         }
2102
2103         QSqlQuery query(db);
2104         if (last == -1) {
2105             query.prepare(queryString("select_messagesAllNew"));
2106         }
2107         else {
2108             query.prepare(queryString("select_messagesAll"));
2109             query.bindValue(":lastmsg", last.toQint64());
2110         }
2111         query.bindValue(":userid", user.toInt());
2112         query.bindValue(":firstmsg", first.toQint64());
2113         query.bindValue(":limit", limit);
2114         safeExec(query);
2115
2116         watchQuery(query);
2117
2118         while (query.next()) {
2119             Message msg(
2120                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2121                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
2122                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2123                 bufferInfoHash[query.value(1).toInt()],
2124                 (Message::Type)query.value(3).toInt(),
2125                 query.value(9).toString(),
2126                 query.value(5).toString(),
2127                 query.value(6).toString(),
2128                 query.value(7).toString(),
2129                 query.value(8).toString(),
2130                 (Message::Flags)query.value(4).toInt());
2131             msg.setMsgId(query.value(0).toLongLong());
2132             messagelist.push_back(std::move(msg));
2133         }
2134     }
2135     db.commit();
2136     unlock();
2137     return messagelist;
2138 }
2139
2140 std::vector<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
2141 {
2142     std::vector<Message> messagelist;
2143
2144     QSqlDatabase db = logDb();
2145     db.transaction();
2146
2147     QHash<BufferId, BufferInfo> bufferInfoHash;
2148     {
2149         QSqlQuery bufferInfoQuery(db);
2150         bufferInfoQuery.prepare(queryString("select_buffers"));
2151         bufferInfoQuery.bindValue(":userid", user.toInt());
2152
2153         lockForRead();
2154         safeExec(bufferInfoQuery);
2155         watchQuery(bufferInfoQuery);
2156         while (bufferInfoQuery.next()) {
2157             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2158                                                bufferInfoQuery.value(1).toInt(),
2159                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2160                                                bufferInfoQuery.value(3).toInt(),
2161                                                bufferInfoQuery.value(4).toString());
2162             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2163         }
2164
2165         QSqlQuery query(db);
2166         if (last == -1) {
2167             query.prepare(queryString("select_messagesAllNew_filtered"));
2168         }
2169         else {
2170             query.prepare(queryString("select_messagesAll_filtered"));
2171             query.bindValue(":lastmsg", last.toQint64());
2172         }
2173         query.bindValue(":userid", user.toInt());
2174         query.bindValue(":firstmsg", first.toQint64());
2175         query.bindValue(":limit", limit);
2176         int typeRaw = type;
2177         query.bindValue(":type", typeRaw);
2178         int flagsRaw = flags;
2179         query.bindValue(":flags", flagsRaw);
2180         safeExec(query);
2181
2182         watchQuery(query);
2183
2184         while (query.next()) {
2185             Message msg(
2186                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2187                 // instead of seconds.  This nets us more precision as well as simplifying
2188                 // 64-bit time.
2189                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2190                 bufferInfoHash[query.value(1).toInt()],
2191                 (Message::Type)query.value(3).toInt(),
2192                 query.value(9).toString(),
2193                 query.value(5).toString(),
2194                 query.value(6).toString(),
2195                 query.value(7).toString(),
2196                 query.value(8).toString(),
2197                 Message::Flags{query.value(4).toInt()});
2198             msg.setMsgId(query.value(0).toLongLong());
2199             messagelist.push_back(std::move(msg));
2200         }
2201     }
2202     db.commit();
2203     unlock();
2204     return messagelist;
2205 }
2206
2207 QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
2208 {
2209     QMap<UserId, QString> authusernames;
2210
2211     QSqlDatabase db = logDb();
2212     db.transaction();
2213     {
2214         QSqlQuery query(db);
2215         query.prepare(queryString("select_all_authusernames"));
2216
2217         lockForRead();
2218         safeExec(query);
2219         watchQuery(query);
2220         while (query.next()) {
2221             authusernames[query.value(0).toInt()] = query.value(1).toString();
2222         }
2223     }
2224     db.commit();
2225     unlock();
2226     return authusernames;
2227 }
2228
2229 QString SqliteStorage::backlogFile()
2230 {
2231     return Quassel::configDirPath() + "quassel-storage.sqlite";
2232 }
2233
2234 bool SqliteStorage::safeExec(QSqlQuery& query, int retryCount)
2235 {
2236     query.exec();
2237
2238     if (!query.lastError().isValid())
2239         return true;
2240
2241     QString nativeErrorCode = query.lastError().nativeErrorCode();
2242
2243     // SQLITE_BUSY         5   /* The database file is locked */
2244     // SQLITE_LOCKED       6   /* A table in the database is locked */
2245     if (nativeErrorCode == QLatin1String{"5"} || nativeErrorCode == QLatin1String{"6"}) {
2246         if (retryCount < _maxRetryCount)
2247             return safeExec(query, retryCount + 1);
2248     }
2249     return false;
2250 }
2251
2252 // ========================================
2253 //  SqliteMigration
2254 // ========================================
2255 SqliteMigrationReader::SqliteMigrationReader()
2256     : SqliteStorage()
2257 {}
2258
2259 void SqliteMigrationReader::setMaxId(MigrationObject mo)
2260 {
2261     QString queryString;
2262     switch (mo) {
2263     case Sender:
2264         queryString = "SELECT max(senderid) FROM sender";
2265         break;
2266     case Backlog:
2267         queryString = "SELECT max(messageid) FROM backlog";
2268         break;
2269     default:
2270         _maxId = 0;
2271         return;
2272     }
2273     QSqlQuery query = logDb().exec(queryString);
2274     query.first();
2275     _maxId = query.value(0).toLongLong();
2276 }
2277
2278 bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
2279 {
2280     setMaxId(mo);
2281
2282     switch (mo) {
2283     case QuasselUser:
2284         newQuery(queryString("migrate_read_quasseluser"), logDb());
2285         break;
2286     case Identity:
2287         newQuery(queryString("migrate_read_identity"), logDb());
2288         break;
2289     case IdentityNick:
2290         newQuery(queryString("migrate_read_identity_nick"), logDb());
2291         break;
2292     case Network:
2293         newQuery(queryString("migrate_read_network"), logDb());
2294         break;
2295     case Buffer:
2296         newQuery(queryString("migrate_read_buffer"), logDb());
2297         break;
2298     case Sender:
2299         newQuery(queryString("migrate_read_sender"), logDb());
2300         bindValue(0, 0);
2301         bindValue(1, stepSize());
2302         break;
2303     case Backlog:
2304         newQuery(queryString("migrate_read_backlog"), logDb());
2305         bindValue(0, 0);
2306         bindValue(1, stepSize());
2307         break;
2308     case IrcServer:
2309         newQuery(queryString("migrate_read_ircserver"), logDb());
2310         break;
2311     case UserSetting:
2312         newQuery(queryString("migrate_read_usersetting"), logDb());
2313         break;
2314     case CoreState:
2315         newQuery(queryString("migrate_read_corestate"), logDb());
2316         break;
2317     }
2318     return exec();
2319 }
2320
2321 bool SqliteMigrationReader::readMo(QuasselUserMO& user)
2322 {
2323     if (!next())
2324         return false;
2325
2326     user.id = value(0).toInt();
2327     user.username = value(1).toString();
2328     user.password = value(2).toString();
2329     user.hashversion = value(3).toInt();
2330     user.authenticator = value(4).toString();
2331     return true;
2332 }
2333
2334 bool SqliteMigrationReader::readMo(IdentityMO& identity)
2335 {
2336     if (!next())
2337         return false;
2338
2339     identity.id = value(0).toInt();
2340     identity.userid = value(1).toInt();
2341     identity.identityname = value(2).toString();
2342     identity.realname = value(3).toString();
2343     identity.awayNick = value(4).toString();
2344     identity.awayNickEnabled = value(5).toInt() == 1 ? true : false;
2345     identity.awayReason = value(6).toString();
2346     identity.awayReasonEnabled = value(7).toInt() == 1 ? true : false;
2347     identity.autoAwayEnabled = value(8).toInt() == 1 ? true : false;
2348     identity.autoAwayTime = value(9).toInt();
2349     identity.autoAwayReason = value(10).toString();
2350     identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
2351     identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
2352     identity.detachAwayReason = value(13).toString();
2353     identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
2354     identity.ident = value(15).toString();
2355     identity.kickReason = value(16).toString();
2356     identity.partReason = value(17).toString();
2357     identity.quitReason = value(18).toString();
2358     identity.sslCert = value(19).toByteArray();
2359     identity.sslKey = value(20).toByteArray();
2360     return true;
2361 }
2362
2363 bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
2364 {
2365     if (!next())
2366         return false;
2367
2368     identityNick.nickid = value(0).toInt();
2369     identityNick.identityId = value(1).toInt();
2370     identityNick.nick = value(2).toString();
2371     return true;
2372 }
2373
2374 bool SqliteMigrationReader::readMo(NetworkMO& network)
2375 {
2376     if (!next())
2377         return false;
2378
2379     network.networkid = value(0).toInt();
2380     network.userid = value(1).toInt();
2381     network.networkname = value(2).toString();
2382     network.identityid = value(3).toInt();
2383     network.encodingcodec = value(4).toString();
2384     network.decodingcodec = value(5).toString();
2385     network.servercodec = value(6).toString();
2386     network.userandomserver = value(7).toInt() == 1 ? true : false;
2387     network.perform = value(8).toString();
2388     network.useautoidentify = value(9).toInt() == 1 ? true : false;
2389     network.autoidentifyservice = value(10).toString();
2390     network.autoidentifypassword = value(11).toString();
2391     network.useautoreconnect = value(12).toInt() == 1 ? true : false;
2392     network.autoreconnectinterval = value(13).toInt();
2393     network.autoreconnectretries = value(14).toInt();
2394     network.unlimitedconnectretries = value(15).toInt() == 1 ? true : false;
2395     network.rejoinchannels = value(16).toInt() == 1 ? true : false;
2396     network.connected = value(17).toInt() == 1 ? true : false;
2397     network.usermode = value(18).toString();
2398     network.awaymessage = value(19).toString();
2399     network.attachperform = value(20).toString();
2400     network.detachperform = value(21).toString();
2401     network.usesasl = value(22).toInt() == 1 ? true : false;
2402     network.saslaccount = value(23).toString();
2403     network.saslpassword = value(24).toString();
2404     // Custom rate limiting
2405     network.usecustommessagerate = value(25).toInt() == 1 ? true : false;
2406     network.messagerateburstsize = value(26).toInt();
2407     network.messageratedelay = value(27).toUInt();
2408     network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false;
2409     return true;
2410 }
2411
2412 bool SqliteMigrationReader::readMo(BufferMO& buffer)
2413 {
2414     if (!next())
2415         return false;
2416
2417     buffer.bufferid = value(0).toInt();
2418     buffer.userid = value(1).toInt();
2419     buffer.groupid = value(2).toInt();
2420     buffer.networkid = value(3).toInt();
2421     buffer.buffername = value(4).toString();
2422     buffer.buffercname = value(5).toString();
2423     buffer.buffertype = value(6).toInt();
2424     buffer.lastmsgid = value(7).toLongLong();
2425     buffer.lastseenmsgid = value(8).toLongLong();
2426     buffer.markerlinemsgid = value(9).toLongLong();
2427     buffer.bufferactivity = value(10).toInt();
2428     buffer.highlightcount = value(11).toInt();
2429     buffer.key = value(12).toString();
2430     buffer.joined = value(13).toInt() == 1 ? true : false;
2431     buffer.cipher = value(14).toString();
2432     return true;
2433 }
2434
2435 bool SqliteMigrationReader::readMo(SenderMO& sender)
2436 {
2437     int skipSteps = 0;
2438     while (!next()) {
2439         if (sender.senderId < _maxId) {
2440             bindValue(0, sender.senderId + (skipSteps * stepSize()));
2441             bindValue(1, sender.senderId + ((skipSteps + 1) * stepSize()));
2442             skipSteps++;
2443             if (!exec())
2444                 return false;
2445         }
2446         else {
2447             return false;
2448         }
2449     }
2450
2451     sender.senderId = value(0).toLongLong();
2452     sender.sender = value(1).toString();
2453     sender.realname = value(2).toString();
2454     sender.avatarurl = value(3).toString();
2455     return true;
2456 }
2457
2458 bool SqliteMigrationReader::readMo(BacklogMO& backlog)
2459 {
2460     qint64 skipSteps = 0;
2461     while (!next()) {
2462         if (backlog.messageid < _maxId) {
2463             bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize()));
2464             bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize()));
2465             skipSteps++;
2466             if (!exec())
2467                 return false;
2468         }
2469         else {
2470             return false;
2471         }
2472     }
2473
2474     backlog.messageid = value(0).toLongLong();
2475     // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2476     // seconds.  This nets us more precision as well as simplifying 64-bit time.
2477     backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC();
2478     backlog.bufferid = value(2).toInt();
2479     backlog.type = value(3).toInt();
2480     backlog.flags = value(4).toInt();
2481     backlog.senderid = value(5).toLongLong();
2482     backlog.senderprefixes = value(6).toString();
2483     backlog.message = value(7).toString();
2484     return true;
2485 }
2486
2487 bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
2488 {
2489     if (!next())
2490         return false;
2491
2492     ircserver.serverid = value(0).toInt();
2493     ircserver.userid = value(1).toInt();
2494     ircserver.networkid = value(2).toInt();
2495     ircserver.hostname = value(3).toString();
2496     ircserver.port = value(4).toInt();
2497     ircserver.password = value(5).toString();
2498     ircserver.ssl = value(6).toInt() == 1 ? true : false;
2499     ircserver.sslversion = value(7).toInt();
2500     ircserver.useproxy = value(8).toInt() == 1 ? true : false;
2501     ircserver.proxytype = value(9).toInt();
2502     ircserver.proxyhost = value(10).toString();
2503     ircserver.proxyport = value(11).toInt();
2504     ircserver.proxyuser = value(12).toString();
2505     ircserver.proxypass = value(13).toString();
2506     ircserver.sslverify = value(14).toInt() == 1 ? true : false;
2507     return true;
2508 }
2509
2510 bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
2511 {
2512     if (!next())
2513         return false;
2514
2515     userSetting.userid = value(0).toInt();
2516     userSetting.settingname = value(1).toString();
2517     userSetting.settingvalue = value(2).toByteArray();
2518
2519     return true;
2520 }
2521
2522 bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
2523 {
2524     if (!next())
2525         return false;
2526
2527     coreState.key = value(0).toString();
2528     coreState.value = value(1).toByteArray();
2529
2530     return true;
2531 }