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