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