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