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