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