026b4dc6f3e77e70c928c6e6738db5aebf339e4c
[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 #ifdef HAVE_SSL
507         query.bindValue(":sslcert", identity.sslCert().toPem());
508         query.bindValue(":sslkey", identity.sslKey().toPem());
509 #else
510         query.bindValue(":sslcert", QByteArray());
511         query.bindValue(":sslkey", QByteArray());
512 #endif
513
514         lockForWrite();
515         safeExec(query);
516
517         identityId = query.lastInsertId().toInt();
518         if (!identityId.isValid()) {
519             watchQuery(query);
520         }
521         else {
522             QSqlQuery deleteNickQuery(db);
523             deleteNickQuery.prepare(queryString("delete_nicks"));
524             deleteNickQuery.bindValue(":identityid", identityId.toInt());
525             safeExec(deleteNickQuery);
526
527             QSqlQuery insertNickQuery(db);
528             insertNickQuery.prepare(queryString("insert_nick"));
529             foreach (QString nick, identity.nicks()) {
530                 insertNickQuery.bindValue(":identityid", identityId.toInt());
531                 insertNickQuery.bindValue(":nick", nick);
532                 safeExec(insertNickQuery);
533             }
534         }
535         db.commit();
536     }
537     unlock();
538     identity.setId(identityId);
539     return identityId;
540 }
541
542 bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity& identity)
543 {
544     QSqlDatabase db = logDb();
545     bool error = false;
546     db.transaction();
547
548     {
549         QSqlQuery checkQuery(db);
550         checkQuery.prepare(queryString("select_checkidentity"));
551         checkQuery.bindValue(":identityid", identity.id().toInt());
552         checkQuery.bindValue(":userid", user.toInt());
553         lockForRead();
554         safeExec(checkQuery);
555
556         // there should be exactly one identity for the given id and user
557         error = (!checkQuery.first() || checkQuery.value(0).toInt() != 1);
558     }
559     if (error) {
560         unlock();
561         return false;
562     }
563
564     {
565         QSqlQuery query(db);
566         query.prepare(queryString("update_identity"));
567         query.bindValue(":identityname", identity.identityName());
568         query.bindValue(":realname", identity.realName());
569         query.bindValue(":awaynick", identity.awayNick());
570         query.bindValue(":awaynickenabled", identity.awayNickEnabled() ? 1 : 0);
571         query.bindValue(":awayreason", identity.awayReason());
572         query.bindValue(":awayreasonenabled", identity.awayReasonEnabled() ? 1 : 0);
573         query.bindValue(":autoawayenabled", identity.awayReasonEnabled() ? 1 : 0);
574         query.bindValue(":autoawaytime", identity.autoAwayTime());
575         query.bindValue(":autoawayreason", identity.autoAwayReason());
576         query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled() ? 1 : 0);
577         query.bindValue(":detachawayenabled", identity.detachAwayEnabled() ? 1 : 0);
578         query.bindValue(":detachawayreason", identity.detachAwayReason());
579         query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled() ? 1 : 0);
580         query.bindValue(":ident", identity.ident());
581         query.bindValue(":kickreason", identity.kickReason());
582         query.bindValue(":partreason", identity.partReason());
583         query.bindValue(":quitreason", identity.quitReason());
584 #ifdef HAVE_SSL
585         query.bindValue(":sslcert", identity.sslCert().toPem());
586         query.bindValue(":sslkey", identity.sslKey().toPem());
587 #else
588         query.bindValue(":sslcert", QByteArray());
589         query.bindValue(":sslkey", QByteArray());
590 #endif
591         query.bindValue(":identityid", identity.id().toInt());
592         safeExec(query);
593         watchQuery(query);
594
595         QSqlQuery deleteNickQuery(db);
596         deleteNickQuery.prepare(queryString("delete_nicks"));
597         deleteNickQuery.bindValue(":identityid", identity.id().toInt());
598         safeExec(deleteNickQuery);
599         watchQuery(deleteNickQuery);
600
601         QSqlQuery insertNickQuery(db);
602         insertNickQuery.prepare(queryString("insert_nick"));
603         foreach (QString nick, identity.nicks()) {
604             insertNickQuery.bindValue(":identityid", identity.id().toInt());
605             insertNickQuery.bindValue(":nick", nick);
606             safeExec(insertNickQuery);
607             watchQuery(insertNickQuery);
608         }
609         db.commit();
610     }
611     unlock();
612     return true;
613 }
614
615 void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
616 {
617     QSqlDatabase db = logDb();
618     db.transaction();
619
620     bool error = false;
621     {
622         QSqlQuery checkQuery(db);
623         checkQuery.prepare(queryString("select_checkidentity"));
624         checkQuery.bindValue(":identityid", identityId.toInt());
625         checkQuery.bindValue(":userid", user.toInt());
626         lockForRead();
627         safeExec(checkQuery);
628
629         // there should be exactly one identity for the given id and user
630         error = (!checkQuery.first() || checkQuery.value(0).toInt() != 1);
631     }
632     if (error) {
633         unlock();
634         return;
635     }
636
637     {
638         QSqlQuery deleteNickQuery(db);
639         deleteNickQuery.prepare(queryString("delete_nicks"));
640         deleteNickQuery.bindValue(":identityid", identityId.toInt());
641         safeExec(deleteNickQuery);
642
643         QSqlQuery deleteIdentityQuery(db);
644         deleteIdentityQuery.prepare(queryString("delete_identity"));
645         deleteIdentityQuery.bindValue(":identityid", identityId.toInt());
646         deleteIdentityQuery.bindValue(":userid", user.toInt());
647         safeExec(deleteIdentityQuery);
648         db.commit();
649     }
650     unlock();
651 }
652
653 std::vector<CoreIdentity> SqliteStorage::identities(UserId user)
654 {
655     std::vector<CoreIdentity> identities;
656     QSqlDatabase db = logDb();
657     db.transaction();
658
659     {
660         QSqlQuery query(db);
661         query.prepare(queryString("select_identities"));
662         query.bindValue(":userid", user.toInt());
663
664         QSqlQuery nickQuery(db);
665         nickQuery.prepare(queryString("select_nicks"));
666
667         lockForRead();
668         safeExec(query);
669
670         while (query.next()) {
671             CoreIdentity identity(IdentityId(query.value(0).toInt()));
672
673             identity.setIdentityName(query.value(1).toString());
674             identity.setRealName(query.value(2).toString());
675             identity.setAwayNick(query.value(3).toString());
676             identity.setAwayNickEnabled(!!query.value(4).toInt());
677             identity.setAwayReason(query.value(5).toString());
678             identity.setAwayReasonEnabled(!!query.value(6).toInt());
679             identity.setAutoAwayEnabled(!!query.value(7).toInt());
680             identity.setAutoAwayTime(query.value(8).toInt());
681             identity.setAutoAwayReason(query.value(9).toString());
682             identity.setAutoAwayReasonEnabled(!!query.value(10).toInt());
683             identity.setDetachAwayEnabled(!!query.value(11).toInt());
684             identity.setDetachAwayReason(query.value(12).toString());
685             identity.setDetachAwayReasonEnabled(!!query.value(13).toInt());
686             identity.setIdent(query.value(14).toString());
687             identity.setKickReason(query.value(15).toString());
688             identity.setPartReason(query.value(16).toString());
689             identity.setQuitReason(query.value(17).toString());
690 #ifdef HAVE_SSL
691             identity.setSslCert(query.value(18).toByteArray());
692             identity.setSslKey(query.value(19).toByteArray());
693 #endif
694
695             nickQuery.bindValue(":identityid", identity.id().toInt());
696             QList<QString> nicks;
697             safeExec(nickQuery);
698             watchQuery(nickQuery);
699             while (nickQuery.next()) {
700                 nicks << nickQuery.value(0).toString();
701             }
702             identity.setNicks(std::move(nicks));
703             identities.push_back(std::move(identity));
704         }
705         db.commit();
706     }
707     unlock();
708     return identities;
709 }
710
711 NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo& info)
712 {
713     NetworkId networkId;
714
715     QSqlDatabase db = logDb();
716     db.transaction();
717
718     bool error = false;
719     {
720         QSqlQuery query(db);
721         query.prepare(queryString("insert_network"));
722         query.bindValue(":userid", user.toInt());
723         bindNetworkInfo(query, info);
724         lockForWrite();
725         safeExec(query);
726         if (!watchQuery(query)) {
727             db.rollback();
728             error = true;
729         }
730         else {
731             networkId = query.lastInsertId().toInt();
732         }
733     }
734     if (error) {
735         unlock();
736         return {};
737     }
738
739     {
740         QSqlQuery insertServersQuery(db);
741         insertServersQuery.prepare(queryString("insert_server"));
742         foreach (Network::Server server, info.serverList) {
743             insertServersQuery.bindValue(":userid", user.toInt());
744             insertServersQuery.bindValue(":networkid", networkId.toInt());
745             bindServerInfo(insertServersQuery, server);
746             safeExec(insertServersQuery);
747             if (!watchQuery(insertServersQuery)) {
748                 db.rollback();
749                 error = true;
750                 break;
751             }
752         }
753         if (!error)
754             db.commit();
755     }
756     unlock();
757     if (error)
758         return {};
759     else
760         return networkId;
761 }
762
763 void SqliteStorage::bindNetworkInfo(QSqlQuery& query, const NetworkInfo& info)
764 {
765     query.bindValue(":networkname", info.networkName);
766     query.bindValue(":identityid", info.identity.toInt());
767     query.bindValue(":encodingcodec", QString(info.codecForEncoding));
768     query.bindValue(":decodingcodec", QString(info.codecForDecoding));
769     query.bindValue(":servercodec", QString(info.codecForServer));
770     query.bindValue(":userandomserver", info.useRandomServer ? 1 : 0);
771     query.bindValue(":perform", info.perform.join("\n"));
772     query.bindValue(":useautoidentify", info.useAutoIdentify ? 1 : 0);
773     query.bindValue(":autoidentifyservice", info.autoIdentifyService);
774     query.bindValue(":autoidentifypassword", info.autoIdentifyPassword);
775     query.bindValue(":usesasl", info.useSasl ? 1 : 0);
776     query.bindValue(":saslaccount", info.saslAccount);
777     query.bindValue(":saslpassword", info.saslPassword);
778     query.bindValue(":useautoreconnect", info.useAutoReconnect ? 1 : 0);
779     query.bindValue(":autoreconnectinterval", info.autoReconnectInterval);
780     query.bindValue(":autoreconnectretries", info.autoReconnectRetries);
781     query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0);
782     query.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0);
783     // Custom rate limiting
784     query.bindValue(":usecustomessagerate", info.useCustomMessageRate ? 1 : 0);
785     query.bindValue(":messagerateburstsize", info.messageRateBurstSize);
786     query.bindValue(":messageratedelay", info.messageRateDelay);
787     query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate ? 1 : 0);
788     if (info.networkId.isValid())
789         query.bindValue(":networkid", info.networkId.toInt());
790 }
791
792 void SqliteStorage::bindServerInfo(QSqlQuery& query, const Network::Server& server)
793 {
794     query.bindValue(":hostname", server.host);
795     query.bindValue(":port", server.port);
796     query.bindValue(":password", server.password);
797     query.bindValue(":ssl", server.useSsl ? 1 : 0);
798     query.bindValue(":sslversion", server.sslVersion);
799     query.bindValue(":useproxy", server.useProxy ? 1 : 0);
800     query.bindValue(":proxytype", server.proxyType);
801     query.bindValue(":proxyhost", server.proxyHost);
802     query.bindValue(":proxyport", server.proxyPort);
803     query.bindValue(":proxyuser", server.proxyUser);
804     query.bindValue(":proxypass", server.proxyPass);
805     query.bindValue(":sslverify", server.sslVerify ? 1 : 0);
806 }
807
808 bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo& info)
809 {
810     QSqlDatabase db = logDb();
811     bool error = false;
812     db.transaction();
813
814     {
815         QSqlQuery updateQuery(db);
816         updateQuery.prepare(queryString("update_network"));
817         updateQuery.bindValue(":userid", user.toInt());
818         bindNetworkInfo(updateQuery, info);
819
820         lockForWrite();
821         safeExec(updateQuery);
822         if (!watchQuery(updateQuery) || updateQuery.numRowsAffected() != 1) {
823             error = true;
824             db.rollback();
825         }
826     }
827     if (error) {
828         unlock();
829         return false;
830     }
831
832     {
833         QSqlQuery dropServersQuery(db);
834         dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
835         dropServersQuery.bindValue(":networkid", info.networkId.toInt());
836         safeExec(dropServersQuery);
837         if (!watchQuery(dropServersQuery)) {
838             error = true;
839             db.rollback();
840         }
841     }
842     if (error) {
843         unlock();
844         return false;
845     }
846
847     {
848         QSqlQuery insertServersQuery(db);
849         insertServersQuery.prepare(queryString("insert_server"));
850         foreach (Network::Server server, info.serverList) {
851             insertServersQuery.bindValue(":userid", user.toInt());
852             insertServersQuery.bindValue(":networkid", info.networkId.toInt());
853             bindServerInfo(insertServersQuery, server);
854             safeExec(insertServersQuery);
855             if (!watchQuery(insertServersQuery)) {
856                 error = true;
857                 db.rollback();
858                 break;
859             }
860         }
861     }
862
863     db.commit();
864     unlock();
865     return !error;
866 }
867
868 bool SqliteStorage::removeNetwork(UserId user, const NetworkId& networkId)
869 {
870     QSqlDatabase db = logDb();
871     bool error = false;
872     db.transaction();
873
874     {
875         QSqlQuery deleteNetworkQuery(db);
876         deleteNetworkQuery.prepare(queryString("delete_network"));
877         deleteNetworkQuery.bindValue(":networkid", networkId.toInt());
878         deleteNetworkQuery.bindValue(":userid", user.toInt());
879         lockForWrite();
880         safeExec(deleteNetworkQuery);
881         if (!watchQuery(deleteNetworkQuery) || deleteNetworkQuery.numRowsAffected() != 1) {
882             error = true;
883             db.rollback();
884         }
885     }
886     if (error) {
887         unlock();
888         return false;
889     }
890
891     {
892         QSqlQuery deleteBacklogQuery(db);
893         deleteBacklogQuery.prepare(queryString("delete_backlog_for_network"));
894         deleteBacklogQuery.bindValue(":networkid", networkId.toInt());
895         safeExec(deleteBacklogQuery);
896         if (!watchQuery(deleteBacklogQuery)) {
897             db.rollback();
898             error = true;
899         }
900     }
901     if (error) {
902         unlock();
903         return false;
904     }
905
906     {
907         QSqlQuery deleteBuffersQuery(db);
908         deleteBuffersQuery.prepare(queryString("delete_buffers_for_network"));
909         deleteBuffersQuery.bindValue(":networkid", networkId.toInt());
910         safeExec(deleteBuffersQuery);
911         if (!watchQuery(deleteBuffersQuery)) {
912             db.rollback();
913             error = true;
914         }
915     }
916     if (error) {
917         unlock();
918         return false;
919     }
920
921     {
922         QSqlQuery deleteServersQuery(db);
923         deleteServersQuery.prepare(queryString("delete_ircservers_for_network"));
924         deleteServersQuery.bindValue(":networkid", networkId.toInt());
925         safeExec(deleteServersQuery);
926         if (!watchQuery(deleteServersQuery)) {
927             db.rollback();
928             error = true;
929         }
930     }
931     if (error) {
932         unlock();
933         return false;
934     }
935
936     db.commit();
937     unlock();
938     return true;
939 }
940
941 std::vector<NetworkInfo> SqliteStorage::networks(UserId user)
942 {
943     std::vector<NetworkInfo> nets;
944
945     QSqlDatabase db = logDb();
946     db.transaction();
947
948     {
949         QSqlQuery networksQuery(db);
950         networksQuery.prepare(queryString("select_networks_for_user"));
951         networksQuery.bindValue(":userid", user.toInt());
952
953         QSqlQuery serversQuery(db);
954         serversQuery.prepare(queryString("select_servers_for_network"));
955
956         lockForRead();
957         safeExec(networksQuery);
958         if (watchQuery(networksQuery)) {
959             while (networksQuery.next()) {
960                 NetworkInfo net;
961                 net.networkId = networksQuery.value(0).toInt();
962                 net.networkName = networksQuery.value(1).toString();
963                 net.identity = networksQuery.value(2).toInt();
964                 net.codecForServer = networksQuery.value(3).toString().toLatin1();
965                 net.codecForEncoding = networksQuery.value(4).toString().toLatin1();
966                 net.codecForDecoding = networksQuery.value(5).toString().toLatin1();
967                 net.useRandomServer = networksQuery.value(6).toInt() == 1 ? true : false;
968                 net.perform = networksQuery.value(7).toString().split("\n");
969                 net.useAutoIdentify = networksQuery.value(8).toInt() == 1 ? true : false;
970                 net.autoIdentifyService = networksQuery.value(9).toString();
971                 net.autoIdentifyPassword = networksQuery.value(10).toString();
972                 net.useAutoReconnect = networksQuery.value(11).toInt() == 1 ? true : false;
973                 net.autoReconnectInterval = networksQuery.value(12).toUInt();
974                 net.autoReconnectRetries = networksQuery.value(13).toInt();
975                 net.unlimitedReconnectRetries = networksQuery.value(14).toInt() == 1 ? true : false;
976                 net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false;
977                 net.useSasl = networksQuery.value(16).toInt() == 1 ? true : false;
978                 net.saslAccount = networksQuery.value(17).toString();
979                 net.saslPassword = networksQuery.value(18).toString();
980                 // Custom rate limiting
981                 net.useCustomMessageRate = networksQuery.value(19).toInt() == 1 ? true : false;
982                 net.messageRateBurstSize = networksQuery.value(20).toUInt();
983                 net.messageRateDelay = networksQuery.value(21).toUInt();
984                 net.unlimitedMessageRate = networksQuery.value(22).toInt() == 1 ? true : false;
985
986                 serversQuery.bindValue(":networkid", net.networkId.toInt());
987                 safeExec(serversQuery);
988                 if (!watchQuery(serversQuery)) {
989                     nets.clear();
990                     break;
991                 }
992                 else {
993                     Network::ServerList servers;
994                     while (serversQuery.next()) {
995                         Network::Server server;
996                         server.host = serversQuery.value(0).toString();
997                         server.port = serversQuery.value(1).toUInt();
998                         server.password = serversQuery.value(2).toString();
999                         server.useSsl = serversQuery.value(3).toInt() == 1 ? true : false;
1000                         server.sslVersion = serversQuery.value(4).toInt();
1001                         server.useProxy = serversQuery.value(5).toInt() == 1 ? true : false;
1002                         server.proxyType = serversQuery.value(6).toInt();
1003                         server.proxyHost = serversQuery.value(7).toString();
1004                         server.proxyPort = serversQuery.value(8).toUInt();
1005                         server.proxyUser = serversQuery.value(9).toString();
1006                         server.proxyPass = serversQuery.value(10).toString();
1007                         server.sslVerify = serversQuery.value(11).toInt() == 1 ? true : false;
1008                         servers << server;
1009                     }
1010                     net.serverList = servers;
1011                     nets.push_back(std::move(net));
1012                 }
1013             }
1014         }
1015     }
1016     db.commit();
1017     unlock();
1018     return nets;
1019 }
1020
1021 std::vector<NetworkId> SqliteStorage::connectedNetworks(UserId user)
1022 {
1023     std::vector<NetworkId> connectedNets;
1024
1025     QSqlDatabase db = logDb();
1026     db.transaction();
1027
1028     {
1029         QSqlQuery query(db);
1030         query.prepare(queryString("select_connected_networks"));
1031         query.bindValue(":userid", user.toInt());
1032         lockForRead();
1033         safeExec(query);
1034         watchQuery(query);
1035
1036         while (query.next()) {
1037             connectedNets.emplace_back(query.value(0).toInt());
1038         }
1039         db.commit();
1040     }
1041     unlock();
1042     return connectedNets;
1043 }
1044
1045 void SqliteStorage::setNetworkConnected(UserId user, const NetworkId& networkId, bool isConnected)
1046 {
1047     QSqlDatabase db = logDb();
1048     db.transaction();
1049
1050     {
1051         QSqlQuery query(db);
1052         query.prepare(queryString("update_network_connected"));
1053         query.bindValue(":userid", user.toInt());
1054         query.bindValue(":networkid", networkId.toInt());
1055         query.bindValue(":connected", isConnected ? 1 : 0);
1056
1057         lockForWrite();
1058         safeExec(query);
1059         watchQuery(query);
1060         db.commit();
1061     }
1062     unlock();
1063 }
1064
1065 QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId& networkId)
1066 {
1067     QHash<QString, QString> persistentChans;
1068
1069     QSqlDatabase db = logDb();
1070     db.transaction();
1071     {
1072         QSqlQuery query(db);
1073         query.prepare(queryString("select_persistent_channels"));
1074         query.bindValue(":userid", user.toInt());
1075         query.bindValue(":networkid", networkId.toInt());
1076
1077         lockForRead();
1078         safeExec(query);
1079         watchQuery(query);
1080         while (query.next()) {
1081             persistentChans[query.value(0).toString()] = query.value(1).toString();
1082         }
1083     }
1084     unlock();
1085     return persistentChans;
1086 }
1087
1088 void SqliteStorage::setChannelPersistent(UserId user, const NetworkId& networkId, const QString& channel, bool isJoined)
1089 {
1090     QSqlDatabase db = logDb();
1091     db.transaction();
1092
1093     {
1094         QSqlQuery query(db);
1095         query.prepare(queryString("update_buffer_persistent_channel"));
1096         query.bindValue(":userid", user.toInt());
1097         query.bindValue(":networkid", networkId.toInt());
1098         query.bindValue(":buffercname", channel.toLower());
1099         query.bindValue(":joined", isJoined ? 1 : 0);
1100
1101         lockForWrite();
1102         safeExec(query);
1103         watchQuery(query);
1104         db.commit();
1105     }
1106     unlock();
1107 }
1108
1109 void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId& networkId, const QString& channel, const QString& key)
1110 {
1111     QSqlDatabase db = logDb();
1112     db.transaction();
1113
1114     {
1115         QSqlQuery query(db);
1116         query.prepare(queryString("update_buffer_set_channel_key"));
1117         query.bindValue(":userid", user.toInt());
1118         query.bindValue(":networkid", networkId.toInt());
1119         query.bindValue(":buffercname", channel.toLower());
1120         query.bindValue(":key", key);
1121
1122         lockForWrite();
1123         safeExec(query);
1124         watchQuery(query);
1125         db.commit();
1126     }
1127     unlock();
1128 }
1129
1130 QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
1131 {
1132     QSqlDatabase db = logDb();
1133     db.transaction();
1134
1135     QString awayMsg;
1136     {
1137         QSqlQuery query(db);
1138         query.prepare(queryString("select_network_awaymsg"));
1139         query.bindValue(":userid", user.toInt());
1140         query.bindValue(":networkid", networkId.toInt());
1141
1142         lockForRead();
1143         safeExec(query);
1144         watchQuery(query);
1145         if (query.first())
1146             awayMsg = query.value(0).toString();
1147         db.commit();
1148     }
1149     unlock();
1150
1151     return awayMsg;
1152 }
1153
1154 void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QString& awayMsg)
1155 {
1156     QSqlDatabase db = logDb();
1157     db.transaction();
1158
1159     {
1160         QSqlQuery query(db);
1161         query.prepare(queryString("update_network_set_awaymsg"));
1162         query.bindValue(":userid", user.toInt());
1163         query.bindValue(":networkid", networkId.toInt());
1164         query.bindValue(":awaymsg", awayMsg);
1165
1166         lockForWrite();
1167         safeExec(query);
1168         watchQuery(query);
1169         db.commit();
1170     }
1171     unlock();
1172 }
1173
1174 QString SqliteStorage::userModes(UserId user, NetworkId networkId)
1175 {
1176     QSqlDatabase db = logDb();
1177     db.transaction();
1178
1179     QString modes;
1180     {
1181         QSqlQuery query(db);
1182         query.prepare(queryString("select_network_usermode"));
1183         query.bindValue(":userid", user.toInt());
1184         query.bindValue(":networkid", networkId.toInt());
1185
1186         lockForRead();
1187         safeExec(query);
1188         watchQuery(query);
1189         if (query.first())
1190             modes = query.value(0).toString();
1191         db.commit();
1192     }
1193     unlock();
1194
1195     return modes;
1196 }
1197
1198 void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString& userModes)
1199 {
1200     QSqlDatabase db = logDb();
1201     db.transaction();
1202
1203     {
1204         QSqlQuery query(db);
1205         query.prepare(queryString("update_network_set_usermode"));
1206         query.bindValue(":userid", user.toInt());
1207         query.bindValue(":networkid", networkId.toInt());
1208         query.bindValue(":usermode", userModes);
1209
1210         lockForWrite();
1211         safeExec(query);
1212         watchQuery(query);
1213         db.commit();
1214     }
1215     unlock();
1216 }
1217
1218 BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId& networkId, BufferInfo::Type type, const QString& buffer, bool create)
1219 {
1220     QSqlDatabase db = logDb();
1221     db.transaction();
1222
1223     BufferInfo bufferInfo;
1224     {
1225         QSqlQuery query(db);
1226         query.prepare(queryString("select_bufferByName"));
1227         query.bindValue(":networkid", networkId.toInt());
1228         query.bindValue(":userid", user.toInt());
1229         query.bindValue(":buffercname", buffer.toLower());
1230
1231         lockForRead();
1232         safeExec(query);
1233
1234         if (query.first()) {
1235             bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
1236             if (query.next()) {
1237                 qCritical() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
1238                 qCritical() << "         Query:" << query.lastQuery();
1239                 qCritical() << "  bound Values:";
1240                 QList<QVariant> list = query.boundValues().values();
1241                 for (int i = 0; i < list.size(); ++i)
1242                     qCritical() << i << ":" << list.at(i).toString().toLatin1().data();
1243                 Q_ASSERT(false);
1244             }
1245         }
1246         else if (create) {
1247             // let's create the buffer
1248             QSqlQuery createQuery(db);
1249             createQuery.prepare(queryString("insert_buffer"));
1250             createQuery.bindValue(":userid", user.toInt());
1251             createQuery.bindValue(":networkid", networkId.toInt());
1252             createQuery.bindValue(":buffertype", (int)type);
1253             createQuery.bindValue(":buffername", buffer);
1254             createQuery.bindValue(":buffercname", buffer.toLower());
1255             createQuery.bindValue(":joined", type & BufferInfo::ChannelBuffer ? 1 : 0);
1256
1257             unlock();
1258             lockForWrite();
1259             safeExec(createQuery);
1260             watchQuery(createQuery);
1261             bufferInfo = BufferInfo(createQuery.lastInsertId().toInt(), networkId, type, 0, buffer);
1262         }
1263     }
1264     db.commit();
1265     unlock();
1266     return bufferInfo;
1267 }
1268
1269 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId& bufferId)
1270 {
1271     QSqlDatabase db = logDb();
1272     db.transaction();
1273
1274     BufferInfo bufferInfo;
1275     {
1276         QSqlQuery query(db);
1277         query.prepare(queryString("select_buffer_by_id"));
1278         query.bindValue(":userid", user.toInt());
1279         query.bindValue(":bufferid", bufferId.toInt());
1280
1281         lockForRead();
1282         safeExec(query);
1283
1284         if (watchQuery(query) && query.first()) {
1285             bufferInfo = BufferInfo(query.value(0).toInt(),
1286                                     query.value(1).toInt(),
1287                                     (BufferInfo::Type)query.value(2).toInt(),
1288                                     0,
1289                                     query.value(4).toString());
1290             Q_ASSERT(!query.next());
1291         }
1292         db.commit();
1293     }
1294     unlock();
1295     return bufferInfo;
1296 }
1297
1298 std::vector<BufferInfo> SqliteStorage::requestBuffers(UserId user)
1299 {
1300     std::vector<BufferInfo> bufferlist;
1301
1302     QSqlDatabase db = logDb();
1303     db.transaction();
1304
1305     {
1306         QSqlQuery query(db);
1307         query.prepare(queryString("select_buffers"));
1308         query.bindValue(":userid", user.toInt());
1309
1310         lockForRead();
1311         safeExec(query);
1312         watchQuery(query);
1313         while (query.next()) {
1314             bufferlist.emplace_back(query.value(0).toInt(),
1315                                     query.value(1).toInt(),
1316                                     (BufferInfo::Type)query.value(2).toInt(),
1317                                     query.value(3).toInt(),
1318                                     query.value(4).toString());
1319         }
1320         db.commit();
1321     }
1322     unlock();
1323
1324     return bufferlist;
1325 }
1326
1327 std::vector<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
1328 {
1329     std::vector<BufferId> bufferList;
1330
1331     QSqlDatabase db = logDb();
1332     db.transaction();
1333
1334     {
1335         QSqlQuery query(db);
1336         query.prepare(queryString("select_buffers_for_network"));
1337         query.bindValue(":networkid", networkId.toInt());
1338         query.bindValue(":userid", user.toInt());
1339
1340         lockForRead();
1341         safeExec(query);
1342         watchQuery(query);
1343         while (query.next()) {
1344             bufferList.emplace_back(query.value(0).toInt());
1345         }
1346         db.commit();
1347     }
1348     unlock();
1349
1350     return bufferList;
1351 }
1352
1353 bool SqliteStorage::removeBuffer(const UserId& user, const BufferId& bufferId)
1354 {
1355     QSqlDatabase db = logDb();
1356     db.transaction();
1357
1358     bool error = false;
1359     {
1360         QSqlQuery delBufferQuery(db);
1361         delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1362         delBufferQuery.bindValue(":bufferid", bufferId.toInt());
1363         delBufferQuery.bindValue(":userid", user.toInt());
1364
1365         lockForWrite();
1366         safeExec(delBufferQuery);
1367
1368         error = (!watchQuery(delBufferQuery) || delBufferQuery.numRowsAffected() != 1);
1369     }
1370
1371     if (error) {
1372         db.rollback();
1373         unlock();
1374         return false;
1375     }
1376
1377     {
1378         QSqlQuery delBacklogQuery(db);
1379         delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
1380         delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
1381
1382         safeExec(delBacklogQuery);
1383         error = !watchQuery(delBacklogQuery);
1384     }
1385
1386     if (error) {
1387         db.rollback();
1388     }
1389     else {
1390         db.commit();
1391     }
1392     unlock();
1393     return !error;
1394 }
1395
1396 bool SqliteStorage::renameBuffer(const UserId& user, const BufferId& bufferId, const QString& newName)
1397 {
1398     QSqlDatabase db = logDb();
1399     db.transaction();
1400
1401     bool error = false;
1402     {
1403         QSqlQuery query(db);
1404         query.prepare(queryString("update_buffer_name"));
1405         query.bindValue(":buffername", newName);
1406         query.bindValue(":buffercname", newName.toLower());
1407         query.bindValue(":bufferid", bufferId.toInt());
1408         query.bindValue(":userid", user.toInt());
1409
1410         lockForWrite();
1411         safeExec(query);
1412
1413         error = query.lastError().isValid();
1414         // unexepcted error occured (19 == constraint violation)
1415         if (error && query.lastError().nativeErrorCode() != QLatin1String{"19"}) {
1416             watchQuery(query);
1417         }
1418         else {
1419             error |= (query.numRowsAffected() != 1);
1420         }
1421     }
1422     if (error) {
1423         db.rollback();
1424     }
1425     else {
1426         db.commit();
1427     }
1428     unlock();
1429     return !error;
1430 }
1431
1432 bool SqliteStorage::mergeBuffersPermanently(const UserId& user, const BufferId& bufferId1, const BufferId& bufferId2)
1433 {
1434     QSqlDatabase db = logDb();
1435     db.transaction();
1436
1437     bool error = false;
1438     {
1439         QSqlQuery checkQuery(db);
1440         checkQuery.prepare(queryString("select_buffers_for_merge"));
1441         checkQuery.bindValue(":oldbufferid", bufferId2.toInt());
1442         checkQuery.bindValue(":newbufferid", bufferId1.toInt());
1443         checkQuery.bindValue(":userid", user.toInt());
1444
1445         lockForRead();
1446         safeExec(checkQuery);
1447         error = (!checkQuery.first() || checkQuery.value(0).toInt() != 2);
1448     }
1449     if (error) {
1450         db.rollback();
1451         unlock();
1452         return false;
1453     }
1454
1455     {
1456         QSqlQuery query(db);
1457         query.prepare(queryString("update_backlog_bufferid"));
1458         query.bindValue(":oldbufferid", bufferId2.toInt());
1459         query.bindValue(":newbufferid", bufferId1.toInt());
1460         safeExec(query);
1461         error = !watchQuery(query);
1462     }
1463     if (error) {
1464         db.rollback();
1465         unlock();
1466         return false;
1467     }
1468
1469     {
1470         QSqlQuery delBufferQuery(db);
1471         delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1472         delBufferQuery.bindValue(":bufferid", bufferId2.toInt());
1473         delBufferQuery.bindValue(":userid", user.toInt());
1474         safeExec(delBufferQuery);
1475         error = !watchQuery(delBufferQuery);
1476     }
1477
1478     if (error) {
1479         db.rollback();
1480     }
1481     else {
1482         db.commit();
1483     }
1484     unlock();
1485     return !error;
1486 }
1487
1488 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
1489 {
1490     QSqlDatabase db = logDb();
1491     db.transaction();
1492
1493     {
1494         QSqlQuery query(db);
1495         query.prepare(queryString("update_buffer_lastseen"));
1496         query.bindValue(":userid", user.toInt());
1497         query.bindValue(":bufferid", bufferId.toInt());
1498         query.bindValue(":lastseenmsgid", msgId.toQint64());
1499
1500         lockForWrite();
1501         safeExec(query);
1502         watchQuery(query);
1503     }
1504     db.commit();
1505     unlock();
1506 }
1507
1508 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
1509 {
1510     QHash<BufferId, MsgId> lastSeenHash;
1511
1512     QSqlDatabase db = logDb();
1513     db.transaction();
1514
1515     bool error = false;
1516     {
1517         QSqlQuery query(db);
1518         query.prepare(queryString("select_buffer_lastseen_messages"));
1519         query.bindValue(":userid", user.toInt());
1520
1521         lockForRead();
1522         safeExec(query);
1523         error = !watchQuery(query);
1524         if (!error) {
1525             while (query.next()) {
1526                 lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong();
1527             }
1528         }
1529     }
1530
1531     db.commit();
1532     unlock();
1533     return lastSeenHash;
1534 }
1535
1536 void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
1537 {
1538     QSqlDatabase db = logDb();
1539     db.transaction();
1540
1541     {
1542         QSqlQuery query(db);
1543         query.prepare(queryString("update_buffer_markerlinemsgid"));
1544         query.bindValue(":userid", user.toInt());
1545         query.bindValue(":bufferid", bufferId.toInt());
1546         query.bindValue(":markerlinemsgid", msgId.toQint64());
1547
1548         lockForWrite();
1549         safeExec(query);
1550         watchQuery(query);
1551     }
1552     db.commit();
1553     unlock();
1554 }
1555
1556 QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
1557 {
1558     QHash<BufferId, MsgId> markerLineHash;
1559
1560     QSqlDatabase db = logDb();
1561     db.transaction();
1562
1563     bool error = false;
1564     {
1565         QSqlQuery query(db);
1566         query.prepare(queryString("select_buffer_markerlinemsgids"));
1567         query.bindValue(":userid", user.toInt());
1568
1569         lockForRead();
1570         safeExec(query);
1571         error = !watchQuery(query);
1572         if (!error) {
1573             while (query.next()) {
1574                 markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong();
1575             }
1576         }
1577     }
1578
1579     db.commit();
1580     unlock();
1581     return markerLineHash;
1582 }
1583
1584 void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::Types bufferActivity)
1585 {
1586     QSqlDatabase db = logDb();
1587     db.transaction();
1588
1589     {
1590         QSqlQuery query(db);
1591         query.prepare(queryString("update_buffer_bufferactivity"));
1592         query.bindValue(":userid", user.toInt());
1593         query.bindValue(":bufferid", bufferId.toInt());
1594         query.bindValue(":bufferactivity", (int)bufferActivity);
1595
1596         lockForWrite();
1597         safeExec(query);
1598         watchQuery(query);
1599     }
1600     db.commit();
1601     unlock();
1602 }
1603
1604 QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
1605 {
1606     QHash<BufferId, Message::Types> bufferActivityHash;
1607
1608     QSqlDatabase db = logDb();
1609     db.transaction();
1610
1611     bool error = false;
1612     {
1613         QSqlQuery query(db);
1614         query.prepare(queryString("select_buffer_bufferactivities"));
1615         query.bindValue(":userid", user.toInt());
1616
1617         lockForRead();
1618         safeExec(query);
1619         error = !watchQuery(query);
1620         if (!error) {
1621             while (query.next()) {
1622                 bufferActivityHash[query.value(0).toInt()] = Message::Types(query.value(1).toInt());
1623             }
1624         }
1625     }
1626
1627     db.commit();
1628     unlock();
1629     return bufferActivityHash;
1630 }
1631
1632 Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId)
1633 {
1634     QSqlDatabase db = logDb();
1635     db.transaction();
1636
1637     Message::Types result = Message::Types(nullptr);
1638     {
1639         QSqlQuery query(db);
1640         query.prepare(queryString("select_buffer_bufferactivity"));
1641         query.bindValue(":bufferid", bufferId.toInt());
1642         query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
1643
1644         lockForRead();
1645         safeExec(query);
1646         if (query.first())
1647             result = Message::Types(query.value(0).toInt());
1648     }
1649
1650     db.commit();
1651     unlock();
1652     return result;
1653 }
1654
1655 QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkId& networkId)
1656 {
1657     QHash<QString, QByteArray> bufferCiphers;
1658
1659     QSqlDatabase db = logDb();
1660     db.transaction();
1661     {
1662         QSqlQuery query(db);
1663         query.prepare(queryString("select_buffer_ciphers"));
1664         query.bindValue(":userid", user.toInt());
1665         query.bindValue(":networkid", networkId.toInt());
1666
1667         lockForRead();
1668         safeExec(query);
1669         watchQuery(query);
1670         while (query.next()) {
1671             bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8());
1672         }
1673     }
1674     unlock();
1675     return bufferCiphers;
1676 }
1677
1678 void SqliteStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher)
1679 {
1680     QSqlDatabase db = logDb();
1681     db.transaction();
1682
1683     {
1684         QSqlQuery query(db);
1685         query.prepare(queryString("update_buffer_cipher"));
1686         query.bindValue(":userid", user.toInt());
1687         query.bindValue(":networkid", networkId.toInt());
1688         query.bindValue(":buffercname", bufferName.toLower());
1689         query.bindValue(":cipher", QString(cipher.toHex()));
1690
1691         lockForWrite();
1692         safeExec(query);
1693         watchQuery(query);
1694         db.commit();
1695     }
1696     unlock();
1697 }
1698
1699 void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count)
1700 {
1701     QSqlDatabase db = logDb();
1702     db.transaction();
1703
1704     {
1705         QSqlQuery query(db);
1706         query.prepare(queryString("update_buffer_highlightcount"));
1707         query.bindValue(":userid", user.toInt());
1708         query.bindValue(":bufferid", bufferId.toInt());
1709         query.bindValue(":highlightcount", count);
1710
1711         lockForWrite();
1712         safeExec(query);
1713         watchQuery(query);
1714     }
1715     db.commit();
1716     unlock();
1717 }
1718
1719 QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
1720 {
1721     QHash<BufferId, int> highlightCountHash;
1722
1723     QSqlDatabase db = logDb();
1724     db.transaction();
1725
1726     bool error = false;
1727     {
1728         QSqlQuery query(db);
1729         query.prepare(queryString("select_buffer_highlightcounts"));
1730         query.bindValue(":userid", user.toInt());
1731
1732         lockForRead();
1733         safeExec(query);
1734         error = !watchQuery(query);
1735         if (!error) {
1736             while (query.next()) {
1737                 highlightCountHash[query.value(0).toInt()] = query.value(1).toInt();
1738             }
1739         }
1740     }
1741
1742     db.commit();
1743     unlock();
1744     return highlightCountHash;
1745 }
1746
1747 int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
1748 {
1749     QSqlDatabase db = logDb();
1750     db.transaction();
1751
1752     int result = 0;
1753     {
1754         QSqlQuery query(db);
1755         query.prepare(queryString("select_buffer_highlightcount"));
1756         query.bindValue(":bufferid", bufferId.toInt());
1757         query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
1758
1759         lockForRead();
1760         safeExec(query);
1761         if (query.first())
1762             result = query.value(0).toInt();
1763     }
1764
1765     db.commit();
1766     unlock();
1767     return result;
1768 }
1769
1770 bool SqliteStorage::logMessage(Message& msg)
1771 {
1772     QSqlDatabase db = logDb();
1773     db.transaction();
1774
1775     bool error = false;
1776     {
1777         QSqlQuery logMessageQuery(db);
1778         logMessageQuery.prepare(queryString("insert_message"));
1779         // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1780         // seconds.  This nets us more precision as well as simplifying 64-bit time.
1781         logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
1782         logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1783         logMessageQuery.bindValue(":type", msg.type());
1784         logMessageQuery.bindValue(":flags", (int)msg.flags());
1785         logMessageQuery.bindValue(":sender", msg.sender());
1786         logMessageQuery.bindValue(":realname", msg.realName());
1787         logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
1788         logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
1789         logMessageQuery.bindValue(":message", msg.contents());
1790
1791         lockForWrite();
1792         safeExec(logMessageQuery);
1793
1794         if (logMessageQuery.lastError().isValid()) {
1795             // constraint violation - must be NOT NULL constraint - probably the sender is missing...
1796             if (logMessageQuery.lastError().nativeErrorCode() == QLatin1String{"19"}) {
1797                 QSqlQuery addSenderQuery(db);
1798                 addSenderQuery.prepare(queryString("insert_sender"));
1799                 addSenderQuery.bindValue(":sender", msg.sender());
1800                 addSenderQuery.bindValue(":realname", msg.realName());
1801                 addSenderQuery.bindValue(":avatarurl", msg.avatarUrl());
1802                 safeExec(addSenderQuery);
1803                 safeExec(logMessageQuery);
1804                 error = !watchQuery(logMessageQuery);
1805             }
1806             else {
1807                 watchQuery(logMessageQuery);
1808             }
1809         }
1810         if (!error) {
1811             MsgId msgId = logMessageQuery.lastInsertId().toLongLong();
1812             if (msgId.isValid()) {
1813                 msg.setMsgId(msgId);
1814             }
1815             else {
1816                 error = true;
1817             }
1818         }
1819     }
1820
1821     if (error) {
1822         db.rollback();
1823     }
1824     else {
1825         db.commit();
1826     }
1827
1828     unlock();
1829     return !error;
1830 }
1831
1832 bool SqliteStorage::logMessages(MessageList& msgs)
1833 {
1834     QSqlDatabase db = logDb();
1835     db.transaction();
1836
1837     {
1838         QSet<SenderData> senders;
1839         QSqlQuery addSenderQuery(db);
1840         addSenderQuery.prepare(queryString("insert_sender"));
1841         lockForWrite();
1842         for (int i = 0; i < msgs.count(); i++) {
1843             auto& msg = msgs.at(i);
1844             SenderData sender = {msg.sender(), msg.realName(), msg.avatarUrl()};
1845             if (senders.contains(sender))
1846                 continue;
1847             senders << sender;
1848
1849             addSenderQuery.bindValue(":sender", sender.sender);
1850             addSenderQuery.bindValue(":realname", sender.realname);
1851             addSenderQuery.bindValue(":avatarurl", sender.avatarurl);
1852             safeExec(addSenderQuery);
1853         }
1854     }
1855
1856     bool error = false;
1857     {
1858         QSqlQuery logMessageQuery(db);
1859         logMessageQuery.prepare(queryString("insert_message"));
1860         for (int i = 0; i < msgs.count(); i++) {
1861             Message& msg = msgs[i];
1862             // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1863             // seconds.  This nets us more precision as well as simplifying 64-bit time.
1864             logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
1865             logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1866             logMessageQuery.bindValue(":type", msg.type());
1867             logMessageQuery.bindValue(":flags", (int)msg.flags());
1868             logMessageQuery.bindValue(":sender", msg.sender());
1869             logMessageQuery.bindValue(":realname", msg.realName());
1870             logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
1871             logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
1872             logMessageQuery.bindValue(":message", msg.contents());
1873
1874             safeExec(logMessageQuery);
1875             if (!watchQuery(logMessageQuery)) {
1876                 error = true;
1877                 break;
1878             }
1879             else {
1880                 msg.setMsgId(logMessageQuery.lastInsertId().toLongLong());
1881             }
1882         }
1883     }
1884
1885     if (error) {
1886         db.rollback();
1887         unlock();
1888         // we had a rollback in the db so we need to reset all msgIds
1889         for (int i = 0; i < msgs.count(); i++) {
1890             msgs[i].setMsgId(MsgId());
1891         }
1892     }
1893     else {
1894         db.commit();
1895         unlock();
1896     }
1897     return !error;
1898 }
1899
1900 std::vector<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
1901 {
1902     std::vector<Message> messagelist;
1903
1904     QSqlDatabase db = logDb();
1905     db.transaction();
1906
1907     bool error = false;
1908     BufferInfo bufferInfo;
1909     {
1910         // code duplication from getBufferInfo:
1911         // this is due to the impossibility of nesting transactions and recursive locking
1912         QSqlQuery bufferInfoQuery(db);
1913         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
1914         bufferInfoQuery.bindValue(":userid", user.toInt());
1915         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
1916
1917         lockForRead();
1918         safeExec(bufferInfoQuery);
1919         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
1920         if (!error) {
1921             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
1922                                     bufferInfoQuery.value(1).toInt(),
1923                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
1924                                     0,
1925                                     bufferInfoQuery.value(4).toString());
1926             error = !bufferInfo.isValid();
1927         }
1928     }
1929     if (error) {
1930         db.rollback();
1931         unlock();
1932         return messagelist;
1933     }
1934
1935     {
1936         QSqlQuery query(db);
1937         if (last == -1 && first == -1) {
1938             query.prepare(queryString("select_messagesNewestK"));
1939         }
1940         else if (last == -1) {
1941             query.prepare(queryString("select_messagesNewerThan"));
1942             query.bindValue(":firstmsg", first.toQint64());
1943         }
1944         else {
1945             query.prepare(queryString("select_messagesRange"));
1946             query.bindValue(":lastmsg", last.toQint64());
1947             query.bindValue(":firstmsg", first.toQint64());
1948         }
1949         query.bindValue(":bufferid", bufferId.toInt());
1950         query.bindValue(":limit", limit);
1951
1952         safeExec(query);
1953         watchQuery(query);
1954
1955         while (query.next()) {
1956             Message msg(
1957                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1958                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
1959                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
1960                 bufferInfo,
1961                 (Message::Type)query.value(2).toInt(),
1962                 query.value(8).toString(),
1963                 query.value(4).toString(),
1964                 query.value(5).toString(),
1965                 query.value(6).toString(),
1966                 query.value(7).toString(),
1967                 (Message::Flags)query.value(3).toInt());
1968             msg.setMsgId(query.value(0).toLongLong());
1969             messagelist.push_back(std::move(msg));
1970         }
1971     }
1972     db.commit();
1973     unlock();
1974
1975     return messagelist;
1976 }
1977
1978 std::vector<Message> SqliteStorage::requestMsgsFiltered(
1979     UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
1980 {
1981     std::vector<Message> messagelist;
1982
1983     QSqlDatabase db = logDb();
1984     db.transaction();
1985
1986     bool error = false;
1987     BufferInfo bufferInfo;
1988     {
1989         // code dupication from getBufferInfo:
1990         // this is due to the impossibility of nesting transactions and recursive locking
1991         QSqlQuery bufferInfoQuery(db);
1992         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
1993         bufferInfoQuery.bindValue(":userid", user.toInt());
1994         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
1995
1996         lockForRead();
1997         safeExec(bufferInfoQuery);
1998         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
1999         if (!error) {
2000             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2001                                     bufferInfoQuery.value(1).toInt(),
2002                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2003                                     0,
2004                                     bufferInfoQuery.value(4).toString());
2005             error = !bufferInfo.isValid();
2006         }
2007     }
2008     if (error) {
2009         db.rollback();
2010         unlock();
2011         return messagelist;
2012     }
2013
2014     {
2015         QSqlQuery query(db);
2016         if (last == -1 && first == -1) {
2017             query.prepare(queryString("select_messagesNewestK_filtered"));
2018         }
2019         else if (last == -1) {
2020             query.prepare(queryString("select_messagesNewerThan_filtered"));
2021             query.bindValue(":firstmsg", first.toQint64());
2022         }
2023         else {
2024             query.prepare(queryString("select_messagesRange_filtered"));
2025             query.bindValue(":lastmsg", last.toQint64());
2026             query.bindValue(":firstmsg", first.toQint64());
2027         }
2028         query.bindValue(":bufferid", bufferId.toInt());
2029         query.bindValue(":limit", limit);
2030         int typeRaw = type;
2031         query.bindValue(":type", typeRaw);
2032         int flagsRaw = flags;
2033         query.bindValue(":flags", flagsRaw);
2034
2035         safeExec(query);
2036         watchQuery(query);
2037
2038         while (query.next()) {
2039             Message msg(
2040                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2041                 // instead of seconds.  This nets us more precision as well as simplifying
2042                 // 64-bit time.
2043                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
2044                 bufferInfo,
2045                 (Message::Type)query.value(2).toInt(),
2046                 query.value(8).toString(),
2047                 query.value(4).toString(),
2048                 query.value(5).toString(),
2049                 query.value(6).toString(),
2050                 query.value(7).toString(),
2051                 Message::Flags{query.value(3).toInt()});
2052             msg.setMsgId(query.value(0).toLongLong());
2053             messagelist.push_back(std::move(msg));
2054         }
2055     }
2056     db.commit();
2057     unlock();
2058
2059     return messagelist;
2060 }
2061
2062 std::vector<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
2063 {
2064     std::vector<Message> messagelist;
2065
2066     QSqlDatabase db = logDb();
2067     db.transaction();
2068
2069     QHash<BufferId, BufferInfo> bufferInfoHash;
2070     {
2071         QSqlQuery bufferInfoQuery(db);
2072         bufferInfoQuery.prepare(queryString("select_buffers"));
2073         bufferInfoQuery.bindValue(":userid", user.toInt());
2074
2075         lockForRead();
2076         safeExec(bufferInfoQuery);
2077         watchQuery(bufferInfoQuery);
2078         while (bufferInfoQuery.next()) {
2079             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2080                                                bufferInfoQuery.value(1).toInt(),
2081                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2082                                                bufferInfoQuery.value(3).toInt(),
2083                                                bufferInfoQuery.value(4).toString());
2084             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2085         }
2086
2087         QSqlQuery query(db);
2088         if (last == -1) {
2089             query.prepare(queryString("select_messagesAllNew"));
2090         }
2091         else {
2092             query.prepare(queryString("select_messagesAll"));
2093             query.bindValue(":lastmsg", last.toQint64());
2094         }
2095         query.bindValue(":userid", user.toInt());
2096         query.bindValue(":firstmsg", first.toQint64());
2097         query.bindValue(":limit", limit);
2098         safeExec(query);
2099
2100         watchQuery(query);
2101
2102         while (query.next()) {
2103             Message msg(
2104                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2105                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
2106                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2107                 bufferInfoHash[query.value(1).toInt()],
2108                 (Message::Type)query.value(3).toInt(),
2109                 query.value(9).toString(),
2110                 query.value(5).toString(),
2111                 query.value(6).toString(),
2112                 query.value(7).toString(),
2113                 query.value(8).toString(),
2114                 (Message::Flags)query.value(4).toInt());
2115             msg.setMsgId(query.value(0).toLongLong());
2116             messagelist.push_back(std::move(msg));
2117         }
2118     }
2119     db.commit();
2120     unlock();
2121     return messagelist;
2122 }
2123
2124 std::vector<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
2125 {
2126     std::vector<Message> messagelist;
2127
2128     QSqlDatabase db = logDb();
2129     db.transaction();
2130
2131     QHash<BufferId, BufferInfo> bufferInfoHash;
2132     {
2133         QSqlQuery bufferInfoQuery(db);
2134         bufferInfoQuery.prepare(queryString("select_buffers"));
2135         bufferInfoQuery.bindValue(":userid", user.toInt());
2136
2137         lockForRead();
2138         safeExec(bufferInfoQuery);
2139         watchQuery(bufferInfoQuery);
2140         while (bufferInfoQuery.next()) {
2141             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2142                                                bufferInfoQuery.value(1).toInt(),
2143                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2144                                                bufferInfoQuery.value(3).toInt(),
2145                                                bufferInfoQuery.value(4).toString());
2146             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2147         }
2148
2149         QSqlQuery query(db);
2150         if (last == -1) {
2151             query.prepare(queryString("select_messagesAllNew_filtered"));
2152         }
2153         else {
2154             query.prepare(queryString("select_messagesAll_filtered"));
2155             query.bindValue(":lastmsg", last.toQint64());
2156         }
2157         query.bindValue(":userid", user.toInt());
2158         query.bindValue(":firstmsg", first.toQint64());
2159         query.bindValue(":limit", limit);
2160         int typeRaw = type;
2161         query.bindValue(":type", typeRaw);
2162         int flagsRaw = flags;
2163         query.bindValue(":flags", flagsRaw);
2164         safeExec(query);
2165
2166         watchQuery(query);
2167
2168         while (query.next()) {
2169             Message msg(
2170                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2171                 // instead of seconds.  This nets us more precision as well as simplifying
2172                 // 64-bit time.
2173                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2174                 bufferInfoHash[query.value(1).toInt()],
2175                 (Message::Type)query.value(3).toInt(),
2176                 query.value(9).toString(),
2177                 query.value(5).toString(),
2178                 query.value(6).toString(),
2179                 query.value(7).toString(),
2180                 query.value(8).toString(),
2181                 Message::Flags{query.value(4).toInt()});
2182             msg.setMsgId(query.value(0).toLongLong());
2183             messagelist.push_back(std::move(msg));
2184         }
2185     }
2186     db.commit();
2187     unlock();
2188     return messagelist;
2189 }
2190
2191 QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
2192 {
2193     QMap<UserId, QString> authusernames;
2194
2195     QSqlDatabase db = logDb();
2196     db.transaction();
2197     {
2198         QSqlQuery query(db);
2199         query.prepare(queryString("select_all_authusernames"));
2200
2201         lockForRead();
2202         safeExec(query);
2203         watchQuery(query);
2204         while (query.next()) {
2205             authusernames[query.value(0).toInt()] = query.value(1).toString();
2206         }
2207     }
2208     db.commit();
2209     unlock();
2210     return authusernames;
2211 }
2212
2213 QString SqliteStorage::backlogFile()
2214 {
2215     return Quassel::configDirPath() + "quassel-storage.sqlite";
2216 }
2217
2218 bool SqliteStorage::safeExec(QSqlQuery& query, int retryCount)
2219 {
2220     query.exec();
2221
2222     if (!query.lastError().isValid())
2223         return true;
2224
2225     QString nativeErrorCode = query.lastError().nativeErrorCode();
2226
2227     // SQLITE_BUSY         5   /* The database file is locked */
2228     // SQLITE_LOCKED       6   /* A table in the database is locked */
2229     if (nativeErrorCode == QLatin1String{"5"} || nativeErrorCode == QLatin1String{"6"}) {
2230         if (retryCount < _maxRetryCount)
2231             return safeExec(query, retryCount + 1);
2232     }
2233     return false;
2234 }
2235
2236 // ========================================
2237 //  SqliteMigration
2238 // ========================================
2239 SqliteMigrationReader::SqliteMigrationReader()
2240     : SqliteStorage()
2241 {}
2242
2243 void SqliteMigrationReader::setMaxId(MigrationObject mo)
2244 {
2245     QString queryString;
2246     switch (mo) {
2247     case Sender:
2248         queryString = "SELECT max(senderid) FROM sender";
2249         break;
2250     case Backlog:
2251         queryString = "SELECT max(messageid) FROM backlog";
2252         break;
2253     default:
2254         _maxId = 0;
2255         return;
2256     }
2257     QSqlQuery query = logDb().exec(queryString);
2258     query.first();
2259     _maxId = query.value(0).toLongLong();
2260 }
2261
2262 bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
2263 {
2264     setMaxId(mo);
2265
2266     switch (mo) {
2267     case QuasselUser:
2268         newQuery(queryString("migrate_read_quasseluser"), logDb());
2269         break;
2270     case Identity:
2271         newQuery(queryString("migrate_read_identity"), logDb());
2272         break;
2273     case IdentityNick:
2274         newQuery(queryString("migrate_read_identity_nick"), logDb());
2275         break;
2276     case Network:
2277         newQuery(queryString("migrate_read_network"), logDb());
2278         break;
2279     case Buffer:
2280         newQuery(queryString("migrate_read_buffer"), logDb());
2281         break;
2282     case Sender:
2283         newQuery(queryString("migrate_read_sender"), logDb());
2284         bindValue(0, 0);
2285         bindValue(1, stepSize());
2286         break;
2287     case Backlog:
2288         newQuery(queryString("migrate_read_backlog"), logDb());
2289         bindValue(0, 0);
2290         bindValue(1, stepSize());
2291         break;
2292     case IrcServer:
2293         newQuery(queryString("migrate_read_ircserver"), logDb());
2294         break;
2295     case UserSetting:
2296         newQuery(queryString("migrate_read_usersetting"), logDb());
2297         break;
2298     case CoreState:
2299         newQuery(queryString("migrate_read_corestate"), logDb());
2300         break;
2301     }
2302     return exec();
2303 }
2304
2305 bool SqliteMigrationReader::readMo(QuasselUserMO& user)
2306 {
2307     if (!next())
2308         return false;
2309
2310     user.id = value(0).toInt();
2311     user.username = value(1).toString();
2312     user.password = value(2).toString();
2313     user.hashversion = value(3).toInt();
2314     user.authenticator = value(4).toString();
2315     return true;
2316 }
2317
2318 bool SqliteMigrationReader::readMo(IdentityMO& identity)
2319 {
2320     if (!next())
2321         return false;
2322
2323     identity.id = value(0).toInt();
2324     identity.userid = value(1).toInt();
2325     identity.identityname = value(2).toString();
2326     identity.realname = value(3).toString();
2327     identity.awayNick = value(4).toString();
2328     identity.awayNickEnabled = value(5).toInt() == 1 ? true : false;
2329     identity.awayReason = value(6).toString();
2330     identity.awayReasonEnabled = value(7).toInt() == 1 ? true : false;
2331     identity.autoAwayEnabled = value(8).toInt() == 1 ? true : false;
2332     identity.autoAwayTime = value(9).toInt();
2333     identity.autoAwayReason = value(10).toString();
2334     identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
2335     identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
2336     identity.detachAwayReason = value(13).toString();
2337     identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
2338     identity.ident = value(15).toString();
2339     identity.kickReason = value(16).toString();
2340     identity.partReason = value(17).toString();
2341     identity.quitReason = value(18).toString();
2342     identity.sslCert = value(19).toByteArray();
2343     identity.sslKey = value(20).toByteArray();
2344     return true;
2345 }
2346
2347 bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
2348 {
2349     if (!next())
2350         return false;
2351
2352     identityNick.nickid = value(0).toInt();
2353     identityNick.identityId = value(1).toInt();
2354     identityNick.nick = value(2).toString();
2355     return true;
2356 }
2357
2358 bool SqliteMigrationReader::readMo(NetworkMO& network)
2359 {
2360     if (!next())
2361         return false;
2362
2363     network.networkid = value(0).toInt();
2364     network.userid = value(1).toInt();
2365     network.networkname = value(2).toString();
2366     network.identityid = value(3).toInt();
2367     network.encodingcodec = value(4).toString();
2368     network.decodingcodec = value(5).toString();
2369     network.servercodec = value(6).toString();
2370     network.userandomserver = value(7).toInt() == 1 ? true : false;
2371     network.perform = value(8).toString();
2372     network.useautoidentify = value(9).toInt() == 1 ? true : false;
2373     network.autoidentifyservice = value(10).toString();
2374     network.autoidentifypassword = value(11).toString();
2375     network.useautoreconnect = value(12).toInt() == 1 ? true : false;
2376     network.autoreconnectinterval = value(13).toInt();
2377     network.autoreconnectretries = value(14).toInt();
2378     network.unlimitedconnectretries = value(15).toInt() == 1 ? true : false;
2379     network.rejoinchannels = value(16).toInt() == 1 ? true : false;
2380     network.connected = value(17).toInt() == 1 ? true : false;
2381     network.usermode = value(18).toString();
2382     network.awaymessage = value(19).toString();
2383     network.attachperform = value(20).toString();
2384     network.detachperform = value(21).toString();
2385     network.usesasl = value(22).toInt() == 1 ? true : false;
2386     network.saslaccount = value(23).toString();
2387     network.saslpassword = value(24).toString();
2388     // Custom rate limiting
2389     network.usecustommessagerate = value(25).toInt() == 1 ? true : false;
2390     network.messagerateburstsize = value(26).toInt();
2391     network.messageratedelay = value(27).toUInt();
2392     network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false;
2393     return true;
2394 }
2395
2396 bool SqliteMigrationReader::readMo(BufferMO& buffer)
2397 {
2398     if (!next())
2399         return false;
2400
2401     buffer.bufferid = value(0).toInt();
2402     buffer.userid = value(1).toInt();
2403     buffer.groupid = value(2).toInt();
2404     buffer.networkid = value(3).toInt();
2405     buffer.buffername = value(4).toString();
2406     buffer.buffercname = value(5).toString();
2407     buffer.buffertype = value(6).toInt();
2408     buffer.lastmsgid = value(7).toLongLong();
2409     buffer.lastseenmsgid = value(8).toLongLong();
2410     buffer.markerlinemsgid = value(9).toLongLong();
2411     buffer.bufferactivity = value(10).toInt();
2412     buffer.highlightcount = value(11).toInt();
2413     buffer.key = value(12).toString();
2414     buffer.joined = value(13).toInt() == 1 ? true : false;
2415     buffer.cipher = value(14).toString();
2416     return true;
2417 }
2418
2419 bool SqliteMigrationReader::readMo(SenderMO& sender)
2420 {
2421     int skipSteps = 0;
2422     while (!next()) {
2423         if (sender.senderId < _maxId) {
2424             bindValue(0, sender.senderId + (skipSteps * stepSize()));
2425             bindValue(1, sender.senderId + ((skipSteps + 1) * stepSize()));
2426             skipSteps++;
2427             if (!exec())
2428                 return false;
2429         }
2430         else {
2431             return false;
2432         }
2433     }
2434
2435     sender.senderId = value(0).toLongLong();
2436     sender.sender = value(1).toString();
2437     sender.realname = value(2).toString();
2438     sender.avatarurl = value(3).toString();
2439     return true;
2440 }
2441
2442 bool SqliteMigrationReader::readMo(BacklogMO& backlog)
2443 {
2444     qint64 skipSteps = 0;
2445     while (!next()) {
2446         if (backlog.messageid < _maxId) {
2447             bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize()));
2448             bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize()));
2449             skipSteps++;
2450             if (!exec())
2451                 return false;
2452         }
2453         else {
2454             return false;
2455         }
2456     }
2457
2458     backlog.messageid = value(0).toLongLong();
2459     // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2460     // seconds.  This nets us more precision as well as simplifying 64-bit time.
2461     backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC();
2462     backlog.bufferid = value(2).toInt();
2463     backlog.type = value(3).toInt();
2464     backlog.flags = value(4).toInt();
2465     backlog.senderid = value(5).toLongLong();
2466     backlog.senderprefixes = value(6).toString();
2467     backlog.message = value(7).toString();
2468     return true;
2469 }
2470
2471 bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
2472 {
2473     if (!next())
2474         return false;
2475
2476     ircserver.serverid = value(0).toInt();
2477     ircserver.userid = value(1).toInt();
2478     ircserver.networkid = value(2).toInt();
2479     ircserver.hostname = value(3).toString();
2480     ircserver.port = value(4).toInt();
2481     ircserver.password = value(5).toString();
2482     ircserver.ssl = value(6).toInt() == 1 ? true : false;
2483     ircserver.sslversion = value(7).toInt();
2484     ircserver.useproxy = value(8).toInt() == 1 ? true : false;
2485     ircserver.proxytype = value(9).toInt();
2486     ircserver.proxyhost = value(10).toString();
2487     ircserver.proxyport = value(11).toInt();
2488     ircserver.proxyuser = value(12).toString();
2489     ircserver.proxypass = value(13).toString();
2490     ircserver.sslverify = value(14).toInt() == 1 ? true : false;
2491     return true;
2492 }
2493
2494 bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
2495 {
2496     if (!next())
2497         return false;
2498
2499     userSetting.userid = value(0).toInt();
2500     userSetting.settingname = value(1).toString();
2501     userSetting.settingvalue = value(2).toByteArray();
2502
2503     return true;
2504 }
2505
2506 bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
2507 {
2508     if (!next())
2509         return false;
2510
2511     coreState.key = value(0).toString();
2512     coreState.value = value(1).toByteArray();
2513
2514     return true;
2515 }