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