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