dc5b546713da8659a09e5eb878fca75568da222f
[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 QHash<BufferId, MsgId> SqliteStorage::bufferLastMsgIds(UserId user)
1489 {
1490     QHash<BufferId, MsgId> lastMsgHash;
1491
1492     QSqlDatabase db = logDb();
1493     db.transaction();
1494
1495     bool error = false;
1496     {
1497         QSqlQuery query(db);
1498         query.prepare(queryString("select_buffer_last_messages"));
1499         query.bindValue(":userid", user.toInt());
1500
1501         lockForRead();
1502         safeExec(query);
1503         error = !watchQuery(query);
1504         if (!error) {
1505             while (query.next()) {
1506                 lastMsgHash[query.value(0).toInt()] = query.value(1).toLongLong();
1507             }
1508         }
1509     }
1510
1511     db.commit();
1512     unlock();
1513     return lastMsgHash;
1514 }
1515
1516 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
1517 {
1518     QSqlDatabase db = logDb();
1519     db.transaction();
1520
1521     {
1522         QSqlQuery query(db);
1523         query.prepare(queryString("update_buffer_lastseen"));
1524         query.bindValue(":userid", user.toInt());
1525         query.bindValue(":bufferid", bufferId.toInt());
1526         query.bindValue(":lastseenmsgid", msgId.toQint64());
1527
1528         lockForWrite();
1529         safeExec(query);
1530         watchQuery(query);
1531     }
1532     db.commit();
1533     unlock();
1534 }
1535
1536 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
1537 {
1538     QHash<BufferId, MsgId> lastSeenHash;
1539
1540     QSqlDatabase db = logDb();
1541     db.transaction();
1542
1543     bool error = false;
1544     {
1545         QSqlQuery query(db);
1546         query.prepare(queryString("select_buffer_lastseen_messages"));
1547         query.bindValue(":userid", user.toInt());
1548
1549         lockForRead();
1550         safeExec(query);
1551         error = !watchQuery(query);
1552         if (!error) {
1553             while (query.next()) {
1554                 lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong();
1555             }
1556         }
1557     }
1558
1559     db.commit();
1560     unlock();
1561     return lastSeenHash;
1562 }
1563
1564 void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
1565 {
1566     QSqlDatabase db = logDb();
1567     db.transaction();
1568
1569     {
1570         QSqlQuery query(db);
1571         query.prepare(queryString("update_buffer_markerlinemsgid"));
1572         query.bindValue(":userid", user.toInt());
1573         query.bindValue(":bufferid", bufferId.toInt());
1574         query.bindValue(":markerlinemsgid", msgId.toQint64());
1575
1576         lockForWrite();
1577         safeExec(query);
1578         watchQuery(query);
1579     }
1580     db.commit();
1581     unlock();
1582 }
1583
1584 QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
1585 {
1586     QHash<BufferId, MsgId> markerLineHash;
1587
1588     QSqlDatabase db = logDb();
1589     db.transaction();
1590
1591     bool error = false;
1592     {
1593         QSqlQuery query(db);
1594         query.prepare(queryString("select_buffer_markerlinemsgids"));
1595         query.bindValue(":userid", user.toInt());
1596
1597         lockForRead();
1598         safeExec(query);
1599         error = !watchQuery(query);
1600         if (!error) {
1601             while (query.next()) {
1602                 markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong();
1603             }
1604         }
1605     }
1606
1607     db.commit();
1608     unlock();
1609     return markerLineHash;
1610 }
1611
1612 void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::Types bufferActivity)
1613 {
1614     QSqlDatabase db = logDb();
1615     db.transaction();
1616
1617     {
1618         QSqlQuery query(db);
1619         query.prepare(queryString("update_buffer_bufferactivity"));
1620         query.bindValue(":userid", user.toInt());
1621         query.bindValue(":bufferid", bufferId.toInt());
1622         query.bindValue(":bufferactivity", (int)bufferActivity);
1623
1624         lockForWrite();
1625         safeExec(query);
1626         watchQuery(query);
1627     }
1628     db.commit();
1629     unlock();
1630 }
1631
1632 QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
1633 {
1634     QHash<BufferId, Message::Types> bufferActivityHash;
1635
1636     QSqlDatabase db = logDb();
1637     db.transaction();
1638
1639     bool error = false;
1640     {
1641         QSqlQuery query(db);
1642         query.prepare(queryString("select_buffer_bufferactivities"));
1643         query.bindValue(":userid", user.toInt());
1644
1645         lockForRead();
1646         safeExec(query);
1647         error = !watchQuery(query);
1648         if (!error) {
1649             while (query.next()) {
1650                 bufferActivityHash[query.value(0).toInt()] = Message::Types(query.value(1).toInt());
1651             }
1652         }
1653     }
1654
1655     db.commit();
1656     unlock();
1657     return bufferActivityHash;
1658 }
1659
1660 Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId)
1661 {
1662     QSqlDatabase db = logDb();
1663     db.transaction();
1664
1665     Message::Types result = Message::Types(nullptr);
1666     {
1667         QSqlQuery query(db);
1668         query.prepare(queryString("select_buffer_bufferactivity"));
1669         query.bindValue(":bufferid", bufferId.toInt());
1670         query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
1671
1672         lockForRead();
1673         safeExec(query);
1674         if (query.first())
1675             result = Message::Types(query.value(0).toInt());
1676     }
1677
1678     db.commit();
1679     unlock();
1680     return result;
1681 }
1682
1683 QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkId& networkId)
1684 {
1685     QHash<QString, QByteArray> bufferCiphers;
1686
1687     QSqlDatabase db = logDb();
1688     db.transaction();
1689     {
1690         QSqlQuery query(db);
1691         query.prepare(queryString("select_buffer_ciphers"));
1692         query.bindValue(":userid", user.toInt());
1693         query.bindValue(":networkid", networkId.toInt());
1694
1695         lockForRead();
1696         safeExec(query);
1697         watchQuery(query);
1698         while (query.next()) {
1699             bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8());
1700         }
1701     }
1702     unlock();
1703     return bufferCiphers;
1704 }
1705
1706 void SqliteStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher)
1707 {
1708     QSqlDatabase db = logDb();
1709     db.transaction();
1710
1711     {
1712         QSqlQuery query(db);
1713         query.prepare(queryString("update_buffer_cipher"));
1714         query.bindValue(":userid", user.toInt());
1715         query.bindValue(":networkid", networkId.toInt());
1716         query.bindValue(":buffercname", bufferName.toLower());
1717         query.bindValue(":cipher", QString(cipher.toHex()));
1718
1719         lockForWrite();
1720         safeExec(query);
1721         watchQuery(query);
1722         db.commit();
1723     }
1724     unlock();
1725 }
1726
1727 void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count)
1728 {
1729     QSqlDatabase db = logDb();
1730     db.transaction();
1731
1732     {
1733         QSqlQuery query(db);
1734         query.prepare(queryString("update_buffer_highlightcount"));
1735         query.bindValue(":userid", user.toInt());
1736         query.bindValue(":bufferid", bufferId.toInt());
1737         query.bindValue(":highlightcount", count);
1738
1739         lockForWrite();
1740         safeExec(query);
1741         watchQuery(query);
1742     }
1743     db.commit();
1744     unlock();
1745 }
1746
1747 QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
1748 {
1749     QHash<BufferId, int> highlightCountHash;
1750
1751     QSqlDatabase db = logDb();
1752     db.transaction();
1753
1754     bool error = false;
1755     {
1756         QSqlQuery query(db);
1757         query.prepare(queryString("select_buffer_highlightcounts"));
1758         query.bindValue(":userid", user.toInt());
1759
1760         lockForRead();
1761         safeExec(query);
1762         error = !watchQuery(query);
1763         if (!error) {
1764             while (query.next()) {
1765                 highlightCountHash[query.value(0).toInt()] = query.value(1).toInt();
1766             }
1767         }
1768     }
1769
1770     db.commit();
1771     unlock();
1772     return highlightCountHash;
1773 }
1774
1775 int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
1776 {
1777     QSqlDatabase db = logDb();
1778     db.transaction();
1779
1780     int result = 0;
1781     {
1782         QSqlQuery query(db);
1783         query.prepare(queryString("select_buffer_highlightcount"));
1784         query.bindValue(":bufferid", bufferId.toInt());
1785         query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
1786
1787         lockForRead();
1788         safeExec(query);
1789         if (query.first())
1790             result = query.value(0).toInt();
1791     }
1792
1793     db.commit();
1794     unlock();
1795     return result;
1796 }
1797
1798 bool SqliteStorage::logMessage(Message& msg)
1799 {
1800     QSqlDatabase db = logDb();
1801     db.transaction();
1802
1803     bool error = false;
1804     {
1805         QSqlQuery logMessageQuery(db);
1806         logMessageQuery.prepare(queryString("insert_message"));
1807         // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1808         // seconds.  This nets us more precision as well as simplifying 64-bit time.
1809         logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
1810         logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1811         logMessageQuery.bindValue(":type", msg.type());
1812         logMessageQuery.bindValue(":flags", (int)msg.flags());
1813         logMessageQuery.bindValue(":sender", msg.sender());
1814         logMessageQuery.bindValue(":realname", msg.realName());
1815         logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
1816         logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
1817         logMessageQuery.bindValue(":message", msg.contents());
1818
1819         lockForWrite();
1820         safeExec(logMessageQuery);
1821
1822         if (logMessageQuery.lastError().isValid()) {
1823             // constraint violation - must be NOT NULL constraint - probably the sender is missing...
1824             if (logMessageQuery.lastError().nativeErrorCode() == QLatin1String{"19"}) {
1825                 QSqlQuery addSenderQuery(db);
1826                 addSenderQuery.prepare(queryString("insert_sender"));
1827                 addSenderQuery.bindValue(":sender", msg.sender());
1828                 addSenderQuery.bindValue(":realname", msg.realName());
1829                 addSenderQuery.bindValue(":avatarurl", msg.avatarUrl());
1830                 safeExec(addSenderQuery);
1831                 safeExec(logMessageQuery);
1832                 error = !watchQuery(logMessageQuery);
1833             }
1834             else {
1835                 watchQuery(logMessageQuery);
1836             }
1837         }
1838         if (!error) {
1839             MsgId msgId = logMessageQuery.lastInsertId().toLongLong();
1840             if (msgId.isValid()) {
1841                 msg.setMsgId(msgId);
1842             }
1843             else {
1844                 error = true;
1845             }
1846         }
1847     }
1848
1849     if (error) {
1850         db.rollback();
1851     }
1852     else {
1853         db.commit();
1854     }
1855
1856     unlock();
1857     return !error;
1858 }
1859
1860 bool SqliteStorage::logMessages(MessageList& msgs)
1861 {
1862     QSqlDatabase db = logDb();
1863     db.transaction();
1864
1865     {
1866         QSet<SenderData> senders;
1867         QSqlQuery addSenderQuery(db);
1868         addSenderQuery.prepare(queryString("insert_sender"));
1869         lockForWrite();
1870         for (int i = 0; i < msgs.count(); i++) {
1871             auto& msg = msgs.at(i);
1872             SenderData sender = {msg.sender(), msg.realName(), msg.avatarUrl()};
1873             if (senders.contains(sender))
1874                 continue;
1875             senders << sender;
1876
1877             addSenderQuery.bindValue(":sender", sender.sender);
1878             addSenderQuery.bindValue(":realname", sender.realname);
1879             addSenderQuery.bindValue(":avatarurl", sender.avatarurl);
1880             safeExec(addSenderQuery);
1881         }
1882     }
1883
1884     bool error = false;
1885     {
1886         QSqlQuery logMessageQuery(db);
1887         logMessageQuery.prepare(queryString("insert_message"));
1888         for (int i = 0; i < msgs.count(); i++) {
1889             Message& msg = msgs[i];
1890             // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1891             // seconds.  This nets us more precision as well as simplifying 64-bit time.
1892             logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
1893             logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1894             logMessageQuery.bindValue(":type", msg.type());
1895             logMessageQuery.bindValue(":flags", (int)msg.flags());
1896             logMessageQuery.bindValue(":sender", msg.sender());
1897             logMessageQuery.bindValue(":realname", msg.realName());
1898             logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
1899             logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
1900             logMessageQuery.bindValue(":message", msg.contents());
1901
1902             safeExec(logMessageQuery);
1903             if (!watchQuery(logMessageQuery)) {
1904                 error = true;
1905                 break;
1906             }
1907             else {
1908                 msg.setMsgId(logMessageQuery.lastInsertId().toLongLong());
1909             }
1910         }
1911     }
1912
1913     if (error) {
1914         db.rollback();
1915         unlock();
1916         // we had a rollback in the db so we need to reset all msgIds
1917         for (int i = 0; i < msgs.count(); i++) {
1918             msgs[i].setMsgId(MsgId());
1919         }
1920     }
1921     else {
1922         db.commit();
1923         unlock();
1924     }
1925     return !error;
1926 }
1927
1928 std::vector<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
1929 {
1930     std::vector<Message> messagelist;
1931
1932     QSqlDatabase db = logDb();
1933     db.transaction();
1934
1935     bool error = false;
1936     BufferInfo bufferInfo;
1937     {
1938         // code duplication from getBufferInfo:
1939         // this is due to the impossibility of nesting transactions and recursive locking
1940         QSqlQuery bufferInfoQuery(db);
1941         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
1942         bufferInfoQuery.bindValue(":userid", user.toInt());
1943         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
1944
1945         lockForRead();
1946         safeExec(bufferInfoQuery);
1947         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
1948         if (!error) {
1949             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
1950                                     bufferInfoQuery.value(1).toInt(),
1951                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
1952                                     0,
1953                                     bufferInfoQuery.value(4).toString());
1954             error = !bufferInfo.isValid();
1955         }
1956     }
1957     if (error) {
1958         db.rollback();
1959         unlock();
1960         return messagelist;
1961     }
1962
1963     {
1964         QSqlQuery query(db);
1965         if (last == -1 && first == -1) {
1966             query.prepare(queryString("select_messagesNewestK"));
1967         }
1968         else if (last == -1) {
1969             query.prepare(queryString("select_messagesNewerThan"));
1970             query.bindValue(":firstmsg", first.toQint64());
1971         }
1972         else {
1973             query.prepare(queryString("select_messagesRange"));
1974             query.bindValue(":lastmsg", last.toQint64());
1975             query.bindValue(":firstmsg", first.toQint64());
1976         }
1977         query.bindValue(":bufferid", bufferId.toInt());
1978         query.bindValue(":limit", limit);
1979
1980         safeExec(query);
1981         watchQuery(query);
1982
1983         while (query.next()) {
1984             Message msg(
1985                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
1986                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
1987                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
1988                 bufferInfo,
1989                 (Message::Type)query.value(2).toInt(),
1990                 query.value(8).toString(),
1991                 query.value(4).toString(),
1992                 query.value(5).toString(),
1993                 query.value(6).toString(),
1994                 query.value(7).toString(),
1995                 (Message::Flags)query.value(3).toInt());
1996             msg.setMsgId(query.value(0).toLongLong());
1997             messagelist.push_back(std::move(msg));
1998         }
1999     }
2000     db.commit();
2001     unlock();
2002
2003     return messagelist;
2004 }
2005
2006 std::vector<Message> SqliteStorage::requestMsgsFiltered(
2007     UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
2008 {
2009     std::vector<Message> messagelist;
2010
2011     QSqlDatabase db = logDb();
2012     db.transaction();
2013
2014     bool error = false;
2015     BufferInfo bufferInfo;
2016     {
2017         // code dupication from getBufferInfo:
2018         // this is due to the impossibility of nesting transactions and recursive locking
2019         QSqlQuery bufferInfoQuery(db);
2020         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
2021         bufferInfoQuery.bindValue(":userid", user.toInt());
2022         bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
2023
2024         lockForRead();
2025         safeExec(bufferInfoQuery);
2026         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
2027         if (!error) {
2028             bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2029                                     bufferInfoQuery.value(1).toInt(),
2030                                     (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2031                                     0,
2032                                     bufferInfoQuery.value(4).toString());
2033             error = !bufferInfo.isValid();
2034         }
2035     }
2036     if (error) {
2037         db.rollback();
2038         unlock();
2039         return messagelist;
2040     }
2041
2042     {
2043         QSqlQuery query(db);
2044         if (last == -1 && first == -1) {
2045             query.prepare(queryString("select_messagesNewestK_filtered"));
2046         }
2047         else if (last == -1) {
2048             query.prepare(queryString("select_messagesNewerThan_filtered"));
2049             query.bindValue(":firstmsg", first.toQint64());
2050         }
2051         else {
2052             query.prepare(queryString("select_messagesRange_filtered"));
2053             query.bindValue(":lastmsg", last.toQint64());
2054             query.bindValue(":firstmsg", first.toQint64());
2055         }
2056         query.bindValue(":bufferid", bufferId.toInt());
2057         query.bindValue(":limit", limit);
2058         int typeRaw = type;
2059         query.bindValue(":type", typeRaw);
2060         int flagsRaw = flags;
2061         query.bindValue(":flags", flagsRaw);
2062
2063         safeExec(query);
2064         watchQuery(query);
2065
2066         while (query.next()) {
2067             Message msg(
2068                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2069                 // instead of seconds.  This nets us more precision as well as simplifying
2070                 // 64-bit time.
2071                 QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
2072                 bufferInfo,
2073                 (Message::Type)query.value(2).toInt(),
2074                 query.value(8).toString(),
2075                 query.value(4).toString(),
2076                 query.value(5).toString(),
2077                 query.value(6).toString(),
2078                 query.value(7).toString(),
2079                 Message::Flags{query.value(3).toInt()});
2080             msg.setMsgId(query.value(0).toLongLong());
2081             messagelist.push_back(std::move(msg));
2082         }
2083     }
2084     db.commit();
2085     unlock();
2086
2087     return messagelist;
2088 }
2089
2090 std::vector<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
2091 {
2092     std::vector<Message> messagelist;
2093
2094     QSqlDatabase db = logDb();
2095     db.transaction();
2096
2097     QHash<BufferId, BufferInfo> bufferInfoHash;
2098     {
2099         QSqlQuery bufferInfoQuery(db);
2100         bufferInfoQuery.prepare(queryString("select_buffers"));
2101         bufferInfoQuery.bindValue(":userid", user.toInt());
2102
2103         lockForRead();
2104         safeExec(bufferInfoQuery);
2105         watchQuery(bufferInfoQuery);
2106         while (bufferInfoQuery.next()) {
2107             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2108                                                bufferInfoQuery.value(1).toInt(),
2109                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2110                                                bufferInfoQuery.value(3).toInt(),
2111                                                bufferInfoQuery.value(4).toString());
2112             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2113         }
2114
2115         QSqlQuery query(db);
2116         if (last == -1) {
2117             query.prepare(queryString("select_messagesAllNew"));
2118         }
2119         else {
2120             query.prepare(queryString("select_messagesAll"));
2121             query.bindValue(":lastmsg", last.toQint64());
2122         }
2123         query.bindValue(":userid", user.toInt());
2124         query.bindValue(":firstmsg", first.toQint64());
2125         query.bindValue(":limit", limit);
2126         safeExec(query);
2127
2128         watchQuery(query);
2129
2130         while (query.next()) {
2131             Message msg(
2132                 // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2133                 // seconds.  This nets us more precision as well as simplifying 64-bit time.
2134                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2135                 bufferInfoHash[query.value(1).toInt()],
2136                 (Message::Type)query.value(3).toInt(),
2137                 query.value(9).toString(),
2138                 query.value(5).toString(),
2139                 query.value(6).toString(),
2140                 query.value(7).toString(),
2141                 query.value(8).toString(),
2142                 (Message::Flags)query.value(4).toInt());
2143             msg.setMsgId(query.value(0).toLongLong());
2144             messagelist.push_back(std::move(msg));
2145         }
2146     }
2147     db.commit();
2148     unlock();
2149     return messagelist;
2150 }
2151
2152 std::vector<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
2153 {
2154     std::vector<Message> messagelist;
2155
2156     QSqlDatabase db = logDb();
2157     db.transaction();
2158
2159     QHash<BufferId, BufferInfo> bufferInfoHash;
2160     {
2161         QSqlQuery bufferInfoQuery(db);
2162         bufferInfoQuery.prepare(queryString("select_buffers"));
2163         bufferInfoQuery.bindValue(":userid", user.toInt());
2164
2165         lockForRead();
2166         safeExec(bufferInfoQuery);
2167         watchQuery(bufferInfoQuery);
2168         while (bufferInfoQuery.next()) {
2169             BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
2170                                                bufferInfoQuery.value(1).toInt(),
2171                                                (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
2172                                                bufferInfoQuery.value(3).toInt(),
2173                                                bufferInfoQuery.value(4).toString());
2174             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
2175         }
2176
2177         QSqlQuery query(db);
2178         if (last == -1) {
2179             query.prepare(queryString("select_messagesAllNew_filtered"));
2180         }
2181         else {
2182             query.prepare(queryString("select_messagesAll_filtered"));
2183             query.bindValue(":lastmsg", last.toQint64());
2184         }
2185         query.bindValue(":userid", user.toInt());
2186         query.bindValue(":firstmsg", first.toQint64());
2187         query.bindValue(":limit", limit);
2188         int typeRaw = type;
2189         query.bindValue(":type", typeRaw);
2190         int flagsRaw = flags;
2191         query.bindValue(":flags", flagsRaw);
2192         safeExec(query);
2193
2194         watchQuery(query);
2195
2196         while (query.next()) {
2197             Message msg(
2198                 // As of SQLite schema version 31, timestamps are stored in milliseconds
2199                 // instead of seconds.  This nets us more precision as well as simplifying
2200                 // 64-bit time.
2201                 QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
2202                 bufferInfoHash[query.value(1).toInt()],
2203                 (Message::Type)query.value(3).toInt(),
2204                 query.value(9).toString(),
2205                 query.value(5).toString(),
2206                 query.value(6).toString(),
2207                 query.value(7).toString(),
2208                 query.value(8).toString(),
2209                 Message::Flags{query.value(4).toInt()});
2210             msg.setMsgId(query.value(0).toLongLong());
2211             messagelist.push_back(std::move(msg));
2212         }
2213     }
2214     db.commit();
2215     unlock();
2216     return messagelist;
2217 }
2218
2219 QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
2220 {
2221     QMap<UserId, QString> authusernames;
2222
2223     QSqlDatabase db = logDb();
2224     db.transaction();
2225     {
2226         QSqlQuery query(db);
2227         query.prepare(queryString("select_all_authusernames"));
2228
2229         lockForRead();
2230         safeExec(query);
2231         watchQuery(query);
2232         while (query.next()) {
2233             authusernames[query.value(0).toInt()] = query.value(1).toString();
2234         }
2235     }
2236     db.commit();
2237     unlock();
2238     return authusernames;
2239 }
2240
2241 QString SqliteStorage::backlogFile()
2242 {
2243     return Quassel::configDirPath() + "quassel-storage.sqlite";
2244 }
2245
2246 bool SqliteStorage::safeExec(QSqlQuery& query, int retryCount)
2247 {
2248     query.exec();
2249
2250     if (!query.lastError().isValid())
2251         return true;
2252
2253     QString nativeErrorCode = query.lastError().nativeErrorCode();
2254
2255     // SQLITE_BUSY         5   /* The database file is locked */
2256     // SQLITE_LOCKED       6   /* A table in the database is locked */
2257     if (nativeErrorCode == QLatin1String{"5"} || nativeErrorCode == QLatin1String{"6"}) {
2258         if (retryCount < _maxRetryCount)
2259             return safeExec(query, retryCount + 1);
2260     }
2261     return false;
2262 }
2263
2264 // ========================================
2265 //  SqliteMigration
2266 // ========================================
2267 SqliteMigrationReader::SqliteMigrationReader()
2268     : SqliteStorage()
2269 {}
2270
2271 void SqliteMigrationReader::setMaxId(MigrationObject mo)
2272 {
2273     QString queryString;
2274     switch (mo) {
2275     case Sender:
2276         queryString = "SELECT max(senderid) FROM sender";
2277         break;
2278     case Backlog:
2279         queryString = "SELECT max(messageid) FROM backlog";
2280         break;
2281     default:
2282         _maxId = 0;
2283         return;
2284     }
2285     QSqlQuery query = logDb().exec(queryString);
2286     query.first();
2287     _maxId = query.value(0).toLongLong();
2288 }
2289
2290 bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
2291 {
2292     setMaxId(mo);
2293
2294     switch (mo) {
2295     case QuasselUser:
2296         newQuery(queryString("migrate_read_quasseluser"), logDb());
2297         break;
2298     case Identity:
2299         newQuery(queryString("migrate_read_identity"), logDb());
2300         break;
2301     case IdentityNick:
2302         newQuery(queryString("migrate_read_identity_nick"), logDb());
2303         break;
2304     case Network:
2305         newQuery(queryString("migrate_read_network"), logDb());
2306         break;
2307     case Buffer:
2308         newQuery(queryString("migrate_read_buffer"), logDb());
2309         break;
2310     case Sender:
2311         newQuery(queryString("migrate_read_sender"), logDb());
2312         bindValue(0, 0);
2313         bindValue(1, stepSize());
2314         break;
2315     case Backlog:
2316         newQuery(queryString("migrate_read_backlog"), logDb());
2317         bindValue(0, 0);
2318         bindValue(1, stepSize());
2319         break;
2320     case IrcServer:
2321         newQuery(queryString("migrate_read_ircserver"), logDb());
2322         break;
2323     case UserSetting:
2324         newQuery(queryString("migrate_read_usersetting"), logDb());
2325         break;
2326     case CoreState:
2327         newQuery(queryString("migrate_read_corestate"), logDb());
2328         break;
2329     }
2330     return exec();
2331 }
2332
2333 bool SqliteMigrationReader::readMo(QuasselUserMO& user)
2334 {
2335     if (!next())
2336         return false;
2337
2338     user.id = value(0).toInt();
2339     user.username = value(1).toString();
2340     user.password = value(2).toString();
2341     user.hashversion = value(3).toInt();
2342     user.authenticator = value(4).toString();
2343     return true;
2344 }
2345
2346 bool SqliteMigrationReader::readMo(IdentityMO& identity)
2347 {
2348     if (!next())
2349         return false;
2350
2351     identity.id = value(0).toInt();
2352     identity.userid = value(1).toInt();
2353     identity.identityname = value(2).toString();
2354     identity.realname = value(3).toString();
2355     identity.awayNick = value(4).toString();
2356     identity.awayNickEnabled = value(5).toInt() == 1 ? true : false;
2357     identity.awayReason = value(6).toString();
2358     identity.awayReasonEnabled = value(7).toInt() == 1 ? true : false;
2359     identity.autoAwayEnabled = value(8).toInt() == 1 ? true : false;
2360     identity.autoAwayTime = value(9).toInt();
2361     identity.autoAwayReason = value(10).toString();
2362     identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
2363     identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
2364     identity.detachAwayReason = value(13).toString();
2365     identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
2366     identity.ident = value(15).toString();
2367     identity.kickReason = value(16).toString();
2368     identity.partReason = value(17).toString();
2369     identity.quitReason = value(18).toString();
2370     identity.sslCert = value(19).toByteArray();
2371     identity.sslKey = value(20).toByteArray();
2372     return true;
2373 }
2374
2375 bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
2376 {
2377     if (!next())
2378         return false;
2379
2380     identityNick.nickid = value(0).toInt();
2381     identityNick.identityId = value(1).toInt();
2382     identityNick.nick = value(2).toString();
2383     return true;
2384 }
2385
2386 bool SqliteMigrationReader::readMo(NetworkMO& network)
2387 {
2388     if (!next())
2389         return false;
2390
2391     network.networkid = value(0).toInt();
2392     network.userid = value(1).toInt();
2393     network.networkname = value(2).toString();
2394     network.identityid = value(3).toInt();
2395     network.encodingcodec = value(4).toString();
2396     network.decodingcodec = value(5).toString();
2397     network.servercodec = value(6).toString();
2398     network.userandomserver = value(7).toInt() == 1 ? true : false;
2399     network.perform = value(8).toString();
2400     network.useautoidentify = value(9).toInt() == 1 ? true : false;
2401     network.autoidentifyservice = value(10).toString();
2402     network.autoidentifypassword = value(11).toString();
2403     network.useautoreconnect = value(12).toInt() == 1 ? true : false;
2404     network.autoreconnectinterval = value(13).toInt();
2405     network.autoreconnectretries = value(14).toInt();
2406     network.unlimitedconnectretries = value(15).toInt() == 1 ? true : false;
2407     network.rejoinchannels = value(16).toInt() == 1 ? true : false;
2408     network.connected = value(17).toInt() == 1 ? true : false;
2409     network.usermode = value(18).toString();
2410     network.awaymessage = value(19).toString();
2411     network.attachperform = value(20).toString();
2412     network.detachperform = value(21).toString();
2413     network.usesasl = value(22).toInt() == 1 ? true : false;
2414     network.saslaccount = value(23).toString();
2415     network.saslpassword = value(24).toString();
2416     // Custom rate limiting
2417     network.usecustommessagerate = value(25).toInt() == 1 ? true : false;
2418     network.messagerateburstsize = value(26).toInt();
2419     network.messageratedelay = value(27).toUInt();
2420     network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false;
2421     return true;
2422 }
2423
2424 bool SqliteMigrationReader::readMo(BufferMO& buffer)
2425 {
2426     if (!next())
2427         return false;
2428
2429     buffer.bufferid = value(0).toInt();
2430     buffer.userid = value(1).toInt();
2431     buffer.groupid = value(2).toInt();
2432     buffer.networkid = value(3).toInt();
2433     buffer.buffername = value(4).toString();
2434     buffer.buffercname = value(5).toString();
2435     buffer.buffertype = value(6).toInt();
2436     buffer.lastmsgid = value(7).toLongLong();
2437     buffer.lastseenmsgid = value(8).toLongLong();
2438     buffer.markerlinemsgid = value(9).toLongLong();
2439     buffer.bufferactivity = value(10).toInt();
2440     buffer.highlightcount = value(11).toInt();
2441     buffer.key = value(12).toString();
2442     buffer.joined = value(13).toInt() == 1 ? true : false;
2443     buffer.cipher = value(14).toString();
2444     return true;
2445 }
2446
2447 bool SqliteMigrationReader::readMo(SenderMO& sender)
2448 {
2449     int skipSteps = 0;
2450     while (!next()) {
2451         if (sender.senderId < _maxId) {
2452             bindValue(0, sender.senderId + (skipSteps * stepSize()));
2453             bindValue(1, sender.senderId + ((skipSteps + 1) * stepSize()));
2454             skipSteps++;
2455             if (!exec())
2456                 return false;
2457         }
2458         else {
2459             return false;
2460         }
2461     }
2462
2463     sender.senderId = value(0).toLongLong();
2464     sender.sender = value(1).toString();
2465     sender.realname = value(2).toString();
2466     sender.avatarurl = value(3).toString();
2467     return true;
2468 }
2469
2470 bool SqliteMigrationReader::readMo(BacklogMO& backlog)
2471 {
2472     qint64 skipSteps = 0;
2473     while (!next()) {
2474         if (backlog.messageid < _maxId) {
2475             bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize()));
2476             bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize()));
2477             skipSteps++;
2478             if (!exec())
2479                 return false;
2480         }
2481         else {
2482             return false;
2483         }
2484     }
2485
2486     backlog.messageid = value(0).toLongLong();
2487     // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
2488     // seconds.  This nets us more precision as well as simplifying 64-bit time.
2489     backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC();
2490     backlog.bufferid = value(2).toInt();
2491     backlog.type = value(3).toInt();
2492     backlog.flags = value(4).toInt();
2493     backlog.senderid = value(5).toLongLong();
2494     backlog.senderprefixes = value(6).toString();
2495     backlog.message = value(7).toString();
2496     return true;
2497 }
2498
2499 bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
2500 {
2501     if (!next())
2502         return false;
2503
2504     ircserver.serverid = value(0).toInt();
2505     ircserver.userid = value(1).toInt();
2506     ircserver.networkid = value(2).toInt();
2507     ircserver.hostname = value(3).toString();
2508     ircserver.port = value(4).toInt();
2509     ircserver.password = value(5).toString();
2510     ircserver.ssl = value(6).toInt() == 1 ? true : false;
2511     ircserver.sslversion = value(7).toInt();
2512     ircserver.useproxy = value(8).toInt() == 1 ? true : false;
2513     ircserver.proxytype = value(9).toInt();
2514     ircserver.proxyhost = value(10).toString();
2515     ircserver.proxyport = value(11).toInt();
2516     ircserver.proxyuser = value(12).toString();
2517     ircserver.proxypass = value(13).toString();
2518     ircserver.sslverify = value(14).toInt() == 1 ? true : false;
2519     return true;
2520 }
2521
2522 bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
2523 {
2524     if (!next())
2525         return false;
2526
2527     userSetting.userid = value(0).toInt();
2528     userSetting.settingname = value(1).toString();
2529     userSetting.settingvalue = value(2).toByteArray();
2530
2531     return true;
2532 }
2533
2534 bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
2535 {
2536     if (!next())
2537         return false;
2538
2539     coreState.key = value(0).toString();
2540     coreState.value = value(1).toByteArray();
2541
2542     return true;
2543 }