[PR-540] Allow loading backlog in reverse direction
[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::requestMsgsForward(
2079     UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
2080 {
2081     std::vector<Message> messagelist;
2082
2083     QSqlDatabase db = logDb();
2084     db.transaction();
2085
2086     bool error = false;
2087     BufferInfo bufferInfo;
2088     {
2089         // code dupication from getBufferInfo:
2090         // this is due to the impossibility of nesting transactions and recursive locking
2091         QSqlQuery bufferInfoQuery(db);
2092         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
2093         bufferInfoQuery.bindValue(":userid", user.toInt());
2094         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
2095
2096         lockForRead();
2097         safeExec(bufferInfoQuery);
2098         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
2099         if (!error) {
2100             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2101                                     bufferInfoQuery.value(1).toInt(),
2102                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2103                                     0,
2104                                     bufferInfoQuery.value(4).toString());
2105             error = !bufferInfo.isValid();
2106         }
2107     }
2108     if (error) {
2109         db.rollback();
2110         unlock();
2111         return messagelist;
2112     }
2113
2114     {
2115         QSqlQuery query(db);
2116         query.prepare(queryString("select_messagesForward"));
2117
2118         if (first == -1) {
2119             query.bindValue(":firstmsg", std::numeric_limits<qint64>::min());
2120         } else {
2121             query.bindValue(":firstmsg", first.toQint64());
2122         }
2123
2124         if (last == -1) {
2125             query.bindValue(":lastmsg", std::numeric_limits<qint64>::max());
2126         } else {
2127             query.bindValue(":lastmsg", last.toQint64());
2128         }
2129
2130         query.bindValue(":bufferid", bufferId.toInt());
2131
2132         int typeRaw = type;
2133         int flagsRaw = flags;
2134         query.bindValue(":type", typeRaw);
2135         query.bindValue(":flags", flagsRaw);
2136
2137         query.bindValue(":limit", limit);
2138
2139         safeExec(query);
2140         watchQuery(query);
2141
2142         while (query.next()) {
2143             Message msg(
2144                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2145                 // instead of seconds.  This nets us more precision as well as simplifying
2146                 // 64-bit time.
2147                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
2148                 bufferInfo,
2149                 (Message::Type)query.value(2).toInt(),
2150                 query.value(8).toString(),
2151                 query.value(4).toString(),
2152                 query.value(5).toString(),
2153                 query.value(6).toString(),
2154                 query.value(7).toString(),
2155                 Message::Flags{query.value(3).toInt()});
2156             msg.setMsgId(query.value(0).toLongLong());
2157             messagelist.push_back(std::move(msg));
2158         }
2159     }
2160     db.commit();
2161     unlock();
2162
2163     return messagelist;
2164 }
2165
2166 std::vector<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
2167 {
2168     std::vector<Message> messagelist;
2169
2170     QSqlDatabase db = logDb();
2171     db.transaction();
2172
2173     QHash<BufferId, BufferInfo> bufferInfoHash;
2174     {
2175         QSqlQuery bufferInfoQuery(db);
2176         bufferInfoQuery.prepare(queryString("select_buffers"));
2177         bufferInfoQuery.bindValue(":userid", user.toInt());
2178
2179         lockForRead();
2180         safeExec(bufferInfoQuery);
2181         watchQuery(bufferInfoQuery);
2182         while (bufferInfoQuery.next()) {
2183             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2184                                                bufferInfoQuery.value(1).toInt(),
2185                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2186                                                bufferInfoQuery.value(3).toInt(),
2187                                                bufferInfoQuery.value(4).toString());
2188             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2189         }
2190
2191         QSqlQuery query(db);
2192         if (last == -1) {
2193             query.prepare(queryString("select_messagesAllNew"));
2194         }
2195         else {
2196             query.prepare(queryString("select_messagesAll"));
2197             query.bindValue(":lastmsg", last.toQint64());
2198         }
2199         query.bindValue(":userid", user.toInt());
2200         query.bindValue(":firstmsg", first.toQint64());
2201         query.bindValue(":limit", limit);
2202         safeExec(query);
2203
2204         watchQuery(query);
2205
2206         while (query.next()) {
2207             Message msg(
2208                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2209                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
2210                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2211                 bufferInfoHash[query.value(1).toInt()],
2212                 (Message::Type)query.value(3).toInt(),
2213                 query.value(9).toString(),
2214                 query.value(5).toString(),
2215                 query.value(6).toString(),
2216                 query.value(7).toString(),
2217                 query.value(8).toString(),
2218                 (Message::Flags)query.value(4).toInt());
2219             msg.setMsgId(query.value(0).toLongLong());
2220             messagelist.push_back(std::move(msg));
2221         }
2222     }
2223     db.commit();
2224     unlock();
2225     return messagelist;
2226 }
2227
2228 std::vector<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
2229 {
2230     std::vector<Message> messagelist;
2231
2232     QSqlDatabase db = logDb();
2233     db.transaction();
2234
2235     QHash<BufferId, BufferInfo> bufferInfoHash;
2236     {
2237         QSqlQuery bufferInfoQuery(db);
2238         bufferInfoQuery.prepare(queryString("select_buffers"));
2239         bufferInfoQuery.bindValue(":userid", user.toInt());
2240
2241         lockForRead();
2242         safeExec(bufferInfoQuery);
2243         watchQuery(bufferInfoQuery);
2244         while (bufferInfoQuery.next()) {
2245             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2246                                                bufferInfoQuery.value(1).toInt(),
2247                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2248                                                bufferInfoQuery.value(3).toInt(),
2249                                                bufferInfoQuery.value(4).toString());
2250             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2251         }
2252
2253         QSqlQuery query(db);
2254         if (last == -1) {
2255             query.prepare(queryString("select_messagesAllNew_filtered"));
2256         }
2257         else {
2258             query.prepare(queryString("select_messagesAll_filtered"));
2259             query.bindValue(":lastmsg", last.toQint64());
2260         }
2261         query.bindValue(":userid", user.toInt());
2262         query.bindValue(":firstmsg", first.toQint64());
2263         query.bindValue(":limit", limit);
2264         int typeRaw = type;
2265         query.bindValue(":type", typeRaw);
2266         int flagsRaw = flags;
2267         query.bindValue(":flags", flagsRaw);
2268         safeExec(query);
2269
2270         watchQuery(query);
2271
2272         while (query.next()) {
2273             Message msg(
2274                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2275                 // instead of seconds.  This nets us more precision as well as simplifying
2276                 // 64-bit time.
2277                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2278                 bufferInfoHash[query.value(1).toInt()],
2279                 (Message::Type)query.value(3).toInt(),
2280                 query.value(9).toString(),
2281                 query.value(5).toString(),
2282                 query.value(6).toString(),
2283                 query.value(7).toString(),
2284                 query.value(8).toString(),
2285                 Message::Flags{query.value(4).toInt()});
2286             msg.setMsgId(query.value(0).toLongLong());
2287             messagelist.push_back(std::move(msg));
2288         }
2289     }
2290     db.commit();
2291     unlock();
2292     return messagelist;
2293 }
2294
2295 QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
2296 {
2297     QMap<UserId, QString> authusernames;
2298
2299     QSqlDatabase db = logDb();
2300     db.transaction();
2301     {
2302         QSqlQuery query(db);
2303         query.prepare(queryString("select_all_authusernames"));
2304
2305         lockForRead();
2306         safeExec(query);
2307         watchQuery(query);
2308         while (query.next()) {
2309             authusernames[query.value(0).toInt()] = query.value(1).toString();
2310         }
2311     }
2312     db.commit();
2313     unlock();
2314     return authusernames;
2315 }
2316
2317 QString SqliteStorage::backlogFile()
2318 {
2319     return Quassel::configDirPath() + "quassel-storage.sqlite";
2320 }
2321
2322 bool SqliteStorage::safeExec(QSqlQuery& query, int retryCount)
2323 {
2324     query.exec();
2325
2326     if (!query.lastError().isValid())
2327         return true;
2328
2329     QString nativeErrorCode = query.lastError().nativeErrorCode();
2330
2331     // SQLITE_BUSY         5   /* The database file is locked */
2332     // SQLITE_LOCKED       6   /* A table in the database is locked */
2333     if (nativeErrorCode == QLatin1String{"5"} || nativeErrorCode == QLatin1String{"6"}) {
2334         if (retryCount < _maxRetryCount)
2335             return safeExec(query, retryCount + 1);
2336     }
2337     return false;
2338 }
2339
2340 // ========================================
2341 //  SqliteMigration
2342 // ========================================
2343 SqliteMigrationReader::SqliteMigrationReader()
2344     : SqliteStorage()
2345 {}
2346
2347 void SqliteMigrationReader::setMaxId(MigrationObject mo)
2348 {
2349     QString queryString;
2350     switch (mo) {
2351     case Sender:
2352         queryString = "SELECT max(senderid) FROM sender";
2353         break;
2354     case Backlog:
2355         queryString = "SELECT max(messageid) FROM backlog";
2356         break;
2357     default:
2358         _maxId = 0;
2359         return;
2360     }
2361     QSqlQuery query = logDb().exec(queryString);
2362     query.first();
2363     _maxId = query.value(0).toLongLong();
2364 }
2365
2366 bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
2367 {
2368     setMaxId(mo);
2369
2370     switch (mo) {
2371     case QuasselUser:
2372         newQuery(queryString("migrate_read_quasseluser"), logDb());
2373         break;
2374     case Identity:
2375         newQuery(queryString("migrate_read_identity"), logDb());
2376         break;
2377     case IdentityNick:
2378         newQuery(queryString("migrate_read_identity_nick"), logDb());
2379         break;
2380     case Network:
2381         newQuery(queryString("migrate_read_network"), logDb());
2382         break;
2383     case Buffer:
2384         newQuery(queryString("migrate_read_buffer"), logDb());
2385         break;
2386     case Sender:
2387         newQuery(queryString("migrate_read_sender"), logDb());
2388         bindValue(0, 0);
2389         bindValue(1, stepSize());
2390         break;
2391     case Backlog:
2392         newQuery(queryString("migrate_read_backlog"), logDb());
2393         bindValue(0, 0);
2394         bindValue(1, stepSize());
2395         break;
2396     case IrcServer:
2397         newQuery(queryString("migrate_read_ircserver"), logDb());
2398         break;
2399     case UserSetting:
2400         newQuery(queryString("migrate_read_usersetting"), logDb());
2401         break;
2402     case CoreState:
2403         newQuery(queryString("migrate_read_corestate"), logDb());
2404         break;
2405     }
2406     return exec();
2407 }
2408
2409 bool SqliteMigrationReader::readMo(QuasselUserMO& user)
2410 {
2411     if (!next())
2412         return false;
2413
2414     user.id = value(0).toInt();
2415     user.username = value(1).toString();
2416     user.password = value(2).toString();
2417     user.hashversion = value(3).toInt();
2418     user.authenticator = value(4).toString();
2419     return true;
2420 }
2421
2422 bool SqliteMigrationReader::readMo(IdentityMO& identity)
2423 {
2424     if (!next())
2425         return false;
2426
2427     identity.id = value(0).toInt();
2428     identity.userid = value(1).toInt();
2429     identity.identityname = value(2).toString();
2430     identity.realname = value(3).toString();
2431     identity.awayNick = value(4).toString();
2432     identity.awayNickEnabled = value(5).toInt() == 1 ? true : false;
2433     identity.awayReason = value(6).toString();
2434     identity.awayReasonEnabled = value(7).toInt() == 1 ? true : false;
2435     identity.autoAwayEnabled = value(8).toInt() == 1 ? true : false;
2436     identity.autoAwayTime = value(9).toInt();
2437     identity.autoAwayReason = value(10).toString();
2438     identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
2439     identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
2440     identity.detachAwayReason = value(13).toString();
2441     identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
2442     identity.ident = value(15).toString();
2443     identity.kickReason = value(16).toString();
2444     identity.partReason = value(17).toString();
2445     identity.quitReason = value(18).toString();
2446     identity.sslCert = value(19).toByteArray();
2447     identity.sslKey = value(20).toByteArray();
2448     return true;
2449 }
2450
2451 bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
2452 {
2453     if (!next())
2454         return false;
2455
2456     identityNick.nickid = value(0).toInt();
2457     identityNick.identityId = value(1).toInt();
2458     identityNick.nick = value(2).toString();
2459     return true;
2460 }
2461
2462 bool SqliteMigrationReader::readMo(NetworkMO& network)
2463 {
2464     if (!next())
2465         return false;
2466
2467     network.networkid = value(0).toInt();
2468     network.userid = value(1).toInt();
2469     network.networkname = value(2).toString();
2470     network.identityid = value(3).toInt();
2471     network.encodingcodec = value(4).toString();
2472     network.decodingcodec = value(5).toString();
2473     network.servercodec = value(6).toString();
2474     network.userandomserver = value(7).toInt() == 1 ? true : false;
2475     network.perform = value(8).toString();
2476     network.useautoidentify = value(9).toInt() == 1 ? true : false;
2477     network.autoidentifyservice = value(10).toString();
2478     network.autoidentifypassword = value(11).toString();
2479     network.useautoreconnect = value(12).toInt() == 1 ? true : false;
2480     network.autoreconnectinterval = value(13).toInt();
2481     network.autoreconnectretries = value(14).toInt();
2482     network.unlimitedconnectretries = value(15).toInt() == 1 ? true : false;
2483     network.rejoinchannels = value(16).toInt() == 1 ? true : false;
2484     network.connected = value(17).toInt() == 1 ? true : false;
2485     network.usermode = value(18).toString();
2486     network.awaymessage = value(19).toString();
2487     network.attachperform = value(20).toString();
2488     network.detachperform = value(21).toString();
2489     network.usesasl = value(22).toInt() == 1 ? true : false;
2490     network.saslaccount = value(23).toString();
2491     network.saslpassword = value(24).toString();
2492     // Custom rate limiting
2493     network.usecustommessagerate = value(25).toInt() == 1 ? true : false;
2494     network.messagerateburstsize = value(26).toInt();
2495     network.messageratedelay = value(27).toUInt();
2496     network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false;
2497     return true;
2498 }
2499
2500 bool SqliteMigrationReader::readMo(BufferMO& buffer)
2501 {
2502     if (!next())
2503         return false;
2504
2505     buffer.bufferid = value(0).toInt();
2506     buffer.userid = value(1).toInt();
2507     buffer.groupid = value(2).toInt();
2508     buffer.networkid = value(3).toInt();
2509     buffer.buffername = value(4).toString();
2510     buffer.buffercname = value(5).toString();
2511     buffer.buffertype = value(6).toInt();
2512     buffer.lastmsgid = value(7).toLongLong();
2513     buffer.lastseenmsgid = value(8).toLongLong();
2514     buffer.markerlinemsgid = value(9).toLongLong();
2515     buffer.bufferactivity = value(10).toInt();
2516     buffer.highlightcount = value(11).toInt();
2517     buffer.key = value(12).toString();
2518     buffer.joined = value(13).toInt() == 1 ? true : false;
2519     buffer.cipher = value(14).toString();
2520     return true;
2521 }
2522
2523 bool SqliteMigrationReader::readMo(SenderMO& sender)
2524 {
2525     int skipSteps = 0;
2526     while (!next()) {
2527         if (sender.senderId < _maxId) {
2528             bindValue(0, sender.senderId + (skipSteps * stepSize()));
2529             bindValue(1, sender.senderId + ((skipSteps + 1) * stepSize()));
2530             skipSteps++;
2531             if (!exec())
2532                 return false;
2533         }
2534         else {
2535             return false;
2536         }
2537     }
2538
2539     sender.senderId = value(0).toLongLong();
2540     sender.sender = value(1).toString();
2541     sender.realname = value(2).toString();
2542     sender.avatarurl = value(3).toString();
2543     return true;
2544 }
2545
2546 bool SqliteMigrationReader::readMo(BacklogMO& backlog)
2547 {
2548     qint64 skipSteps = 0;
2549     while (!next()) {
2550         if (backlog.messageid < _maxId) {
2551             bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize()));
2552             bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize()));
2553             skipSteps++;
2554             if (!exec())
2555                 return false;
2556         }
2557         else {
2558             return false;
2559         }
2560     }
2561
2562     backlog.messageid = value(0).toLongLong();
2563     // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2564     // seconds.  This nets us more precision as well as simplifying 64-bit time.
2565     backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC();
2566     backlog.bufferid = value(2).toInt();
2567     backlog.type = value(3).toInt();
2568     backlog.flags = value(4).toInt();
2569     backlog.senderid = value(5).toLongLong();
2570     backlog.senderprefixes = value(6).toString();
2571     backlog.message = value(7).toString();
2572     return true;
2573 }
2574
2575 bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
2576 {
2577     if (!next())
2578         return false;
2579
2580     ircserver.serverid = value(0).toInt();
2581     ircserver.userid = value(1).toInt();
2582     ircserver.networkid = value(2).toInt();
2583     ircserver.hostname = value(3).toString();
2584     ircserver.port = value(4).toInt();
2585     ircserver.password = value(5).toString();
2586     ircserver.ssl = value(6).toInt() == 1 ? true : false;
2587     ircserver.sslversion = value(7).toInt();
2588     ircserver.useproxy = value(8).toInt() == 1 ? true : false;
2589     ircserver.proxytype = value(9).toInt();
2590     ircserver.proxyhost = value(10).toString();
2591     ircserver.proxyport = value(11).toInt();
2592     ircserver.proxyuser = value(12).toString();
2593     ircserver.proxypass = value(13).toString();
2594     ircserver.sslverify = value(14).toInt() == 1 ? true : false;
2595     return true;
2596 }
2597
2598 bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
2599 {
2600     if (!next())
2601         return false;
2602
2603     userSetting.userid = value(0).toInt();
2604     userSetting.settingname = value(1).toString();
2605     userSetting.settingvalue = value(2).toByteArray();
2606
2607     return true;
2608 }
2609
2610 bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
2611 {
2612     if (!next())
2613         return false;
2614
2615     coreState.key = value(0).toString();
2616     coreState.value = value(1).toByteArray();
2617
2618     return true;
2619 }