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