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