1d732d31760568dc36964c90e391d42c033fe82b
[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
1041       unlock();
1042       lockForWrite();
1043       safeExec(createQuery);
1044       watchQuery(createQuery);
1045       bufferInfo = BufferInfo(createQuery.lastInsertId().toInt(), networkId, type, 0, buffer);
1046     }
1047   }
1048   db.commit();
1049   unlock();
1050   return bufferInfo;
1051 }
1052
1053 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId) {
1054   QSqlDatabase db = logDb();
1055   db.transaction();
1056
1057   BufferInfo bufferInfo;
1058   {
1059     QSqlQuery query(db);
1060     query.prepare(queryString("select_buffer_by_id"));
1061     query.bindValue(":userid", user.toInt());
1062     query.bindValue(":bufferid", bufferId.toInt());
1063
1064     lockForRead();
1065     safeExec(query);
1066
1067     if(watchQuery(query) && query.first()) {
1068       bufferInfo = BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
1069       Q_ASSERT(!query.next());
1070     }
1071     db.commit();
1072   }
1073   unlock();
1074   return bufferInfo;
1075 }
1076
1077 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user) {
1078   QList<BufferInfo> bufferlist;
1079
1080   QSqlDatabase db = logDb();
1081   db.transaction();
1082
1083   {
1084     QSqlQuery query(db);
1085     query.prepare(queryString("select_buffers"));
1086     query.bindValue(":userid", user.toInt());
1087
1088     lockForRead();
1089     safeExec(query);
1090     watchQuery(query);
1091     while(query.next()) {
1092       bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
1093     }
1094     db.commit();
1095   }
1096   unlock();
1097
1098   return bufferlist;
1099 }
1100
1101 QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) {
1102   QList<BufferId> bufferList;
1103
1104   QSqlDatabase db = logDb();
1105   db.transaction();
1106
1107   {
1108     QSqlQuery query(db);
1109     query.prepare(queryString("select_buffers_for_network"));
1110     query.bindValue(":networkid", networkId.toInt());
1111     query.bindValue(":userid", user.toInt());
1112
1113     lockForRead();
1114     safeExec(query);
1115     watchQuery(query);
1116     while(query.next()) {
1117       bufferList << BufferId(query.value(0).toInt());
1118     }
1119     db.commit();
1120   }
1121   unlock();
1122
1123   return bufferList;
1124 }
1125
1126 bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
1127   QSqlDatabase db = logDb();
1128   db.transaction();
1129
1130   bool error = false;
1131   {
1132     QSqlQuery delBufferQuery(db);
1133     delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1134     delBufferQuery.bindValue(":bufferid", bufferId.toInt());
1135     delBufferQuery.bindValue(":userid", user.toInt());
1136
1137     lockForWrite();
1138     safeExec(delBufferQuery);
1139
1140     error = (!watchQuery(delBufferQuery) || delBufferQuery.numRowsAffected() != 1);
1141   }
1142
1143   if(error) {
1144     db.rollback();
1145     unlock();
1146     return false;
1147   }
1148
1149   {
1150     QSqlQuery delBacklogQuery(db);
1151     delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
1152     delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
1153
1154     safeExec(delBacklogQuery);
1155     error = !watchQuery(delBacklogQuery);
1156   }
1157
1158   if(error) {
1159     db.rollback();
1160   } else {
1161     db.commit();
1162   }
1163   unlock();
1164   return !error;
1165 }
1166
1167 bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName) {
1168   QSqlDatabase db = logDb();
1169   db.transaction();
1170
1171   bool error = false;
1172   {
1173     QSqlQuery query(db);
1174     query.prepare(queryString("update_buffer_name"));
1175     query.bindValue(":buffername", newName);
1176     query.bindValue(":buffercname", newName.toLower());
1177     query.bindValue(":bufferid", bufferId.toInt());
1178     query.bindValue(":userid", user.toInt());
1179
1180     lockForWrite();
1181     safeExec(query);
1182
1183     error = query.lastError().isValid();
1184     // unexepcted error occured (19 == constraint violation)
1185     if(error && query.lastError().number() != 19) {
1186       watchQuery(query);
1187     } else {
1188       error |= (query.numRowsAffected() != 1);
1189     }
1190   }
1191   if(error) {
1192     db.rollback();
1193   } else {
1194     db.commit();
1195   }
1196   unlock();
1197   return !error;
1198 }
1199
1200 bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
1201   QSqlDatabase db = logDb();
1202   db.transaction();
1203
1204   bool error = false;
1205   {
1206     QSqlQuery checkQuery(db);
1207     checkQuery.prepare(queryString("select_buffers_for_merge"));
1208     checkQuery.bindValue(":oldbufferid", bufferId2.toInt());
1209     checkQuery.bindValue(":newbufferid", bufferId1.toInt());
1210     checkQuery.bindValue(":userid", user.toInt());
1211
1212     lockForRead();
1213     safeExec(checkQuery);
1214     error = (!checkQuery.first() || checkQuery.value(0).toInt() != 2);
1215   }
1216   if(error) {
1217     db.rollback();
1218     unlock();
1219     return false;
1220   }
1221
1222   {
1223     QSqlQuery query(db);
1224     query.prepare(queryString("update_backlog_bufferid"));
1225     query.bindValue(":oldbufferid", bufferId2.toInt());
1226     query.bindValue(":newbufferid", bufferId1.toInt());
1227     safeExec(query);
1228     error = !watchQuery(query);
1229   }
1230   if(error) {
1231     db.rollback();
1232     unlock();
1233     return false;
1234   }
1235
1236   {
1237     QSqlQuery delBufferQuery(db);
1238     delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1239     delBufferQuery.bindValue(":bufferid", bufferId2.toInt());
1240     delBufferQuery.bindValue(":userid", user.toInt());
1241     safeExec(delBufferQuery);
1242     error = !watchQuery(delBufferQuery);
1243   }
1244
1245   if(error) {
1246     db.rollback();
1247   } else {
1248     db.commit();
1249   }
1250   unlock();
1251   return !error;
1252 }
1253
1254 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
1255   QSqlDatabase db = logDb();
1256   db.transaction();
1257
1258   {
1259     QSqlQuery query(db);
1260     query.prepare(queryString("update_buffer_lastseen"));
1261     query.bindValue(":userid", user.toInt());
1262     query.bindValue(":bufferid", bufferId.toInt());
1263     query.bindValue(":lastseenmsgid", msgId.toInt());
1264
1265     lockForWrite();
1266     safeExec(query);
1267     watchQuery(query);
1268   }
1269   db.commit();
1270   unlock();
1271 }
1272
1273 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user) {
1274   QHash<BufferId, MsgId> lastSeenHash;
1275
1276   QSqlDatabase db = logDb();
1277   db.transaction();
1278
1279   bool error = false;
1280   {
1281     QSqlQuery query(db);
1282     query.prepare(queryString("select_buffer_lastseen_messages"));
1283     query.bindValue(":userid", user.toInt());
1284
1285     lockForRead();
1286     safeExec(query);
1287     error = !watchQuery(query);
1288     if(!error) {
1289       while(query.next()) {
1290         lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
1291       }
1292     }
1293   }
1294
1295   db.commit();
1296   unlock();
1297   return lastSeenHash;
1298 }
1299
1300 void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
1301   QSqlDatabase db = logDb();
1302   db.transaction();
1303
1304   {
1305     QSqlQuery query(db);
1306     query.prepare(queryString("update_buffer_markerlinemsgid"));
1307     query.bindValue(":userid", user.toInt());
1308     query.bindValue(":bufferid", bufferId.toInt());
1309     query.bindValue(":markerlinemsgid", msgId.toInt());
1310
1311     lockForWrite();
1312     safeExec(query);
1313     watchQuery(query);
1314   }
1315   db.commit();
1316   unlock();
1317 }
1318
1319 QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user) {
1320   QHash<BufferId, MsgId> markerLineHash;
1321
1322   QSqlDatabase db = logDb();
1323   db.transaction();
1324
1325   bool error = false;
1326   {
1327     QSqlQuery query(db);
1328     query.prepare(queryString("select_buffer_markerlinemsgids"));
1329     query.bindValue(":userid", user.toInt());
1330
1331     lockForRead();
1332     safeExec(query);
1333     error = !watchQuery(query);
1334     if(!error) {
1335       while(query.next()) {
1336         markerLineHash[query.value(0).toInt()] = query.value(1).toInt();
1337       }
1338     }
1339   }
1340
1341   db.commit();
1342   unlock();
1343   return markerLineHash;
1344 }
1345
1346 bool SqliteStorage::logMessage(Message &msg) {
1347   QSqlDatabase db = logDb();
1348   db.transaction();
1349
1350   bool error = false;
1351   {
1352     QSqlQuery logMessageQuery(db);
1353     logMessageQuery.prepare(queryString("insert_message"));
1354
1355     logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
1356     logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1357     logMessageQuery.bindValue(":type", msg.type());
1358     logMessageQuery.bindValue(":flags", (int)msg.flags());
1359     logMessageQuery.bindValue(":sender", msg.sender());
1360     logMessageQuery.bindValue(":message", msg.contents());
1361
1362     lockForWrite();
1363     safeExec(logMessageQuery);
1364
1365     if(logMessageQuery.lastError().isValid()) {
1366       // constraint violation - must be NOT NULL constraint - probably the sender is missing...
1367       if(logMessageQuery.lastError().number() == 19) {
1368         QSqlQuery addSenderQuery(db);
1369         addSenderQuery.prepare(queryString("insert_sender"));
1370         addSenderQuery.bindValue(":sender", msg.sender());
1371         safeExec(addSenderQuery);
1372         safeExec(logMessageQuery);
1373         error = !watchQuery(logMessageQuery);
1374       } else {
1375         watchQuery(logMessageQuery);
1376       }
1377     }
1378     if(!error) {
1379       MsgId msgId = logMessageQuery.lastInsertId().toInt();
1380       if(msgId.isValid()) {
1381         msg.setMsgId(msgId);
1382       } else {
1383         error = true;
1384       }
1385     }
1386   }
1387
1388   if(error) {
1389     db.rollback();
1390   } else {
1391     db.commit();
1392   }
1393
1394   unlock();
1395   return !error;
1396 }
1397
1398 bool SqliteStorage::logMessages(MessageList &msgs) {
1399   QSqlDatabase db = logDb();
1400   db.transaction();
1401
1402   {
1403     QSet<QString> senders;
1404     QSqlQuery addSenderQuery(db);
1405     addSenderQuery.prepare(queryString("insert_sender"));
1406     lockForWrite();
1407     for(int i = 0; i < msgs.count(); i++) {
1408       const QString &sender = msgs.at(i).sender();
1409       if(senders.contains(sender))
1410         continue;
1411       senders << sender;
1412
1413       addSenderQuery.bindValue(":sender", sender);
1414       safeExec(addSenderQuery);
1415     }
1416   }
1417
1418   bool error = false;
1419   {
1420     QSqlQuery logMessageQuery(db);
1421     logMessageQuery.prepare(queryString("insert_message"));
1422     for(int i = 0; i < msgs.count(); i++) {
1423       Message &msg = msgs[i];
1424
1425       logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
1426       logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
1427       logMessageQuery.bindValue(":type", msg.type());
1428       logMessageQuery.bindValue(":flags", (int)msg.flags());
1429       logMessageQuery.bindValue(":sender", msg.sender());
1430       logMessageQuery.bindValue(":message", msg.contents());
1431
1432       safeExec(logMessageQuery);
1433       if(!watchQuery(logMessageQuery)) {
1434         error = true;
1435         break;
1436       } else {
1437         msg.setMsgId(logMessageQuery.lastInsertId().toInt());
1438       }
1439     }
1440   }
1441
1442   if(error) {
1443     db.rollback();
1444     unlock();
1445     // we had a rollback in the db so we need to reset all msgIds
1446     for(int i = 0; i < msgs.count(); i++) {
1447       msgs[i].setMsgId(MsgId());
1448     }
1449   } else {
1450     db.commit();
1451     unlock();
1452   }
1453   return !error;
1454 }
1455
1456 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
1457   QList<Message> messagelist;
1458
1459   QSqlDatabase db = logDb();
1460   db.transaction();
1461
1462   bool error = false;
1463   BufferInfo bufferInfo;
1464   {
1465     // code dupication from getBufferInfo:
1466     // this is due to the impossibility of nesting transactions and recursive locking
1467     QSqlQuery bufferInfoQuery(db);
1468     bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
1469     bufferInfoQuery.bindValue(":userid", user.toInt());
1470     bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
1471
1472     lockForRead();
1473     safeExec(bufferInfoQuery);
1474     error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
1475     if(!error) {
1476       bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), 0, bufferInfoQuery.value(4).toString());
1477       error = !bufferInfo.isValid();
1478     }
1479   }
1480   if(error) {
1481     db.rollback();
1482     unlock();
1483     return messagelist;
1484   }
1485
1486   {
1487     QSqlQuery query(db);
1488     if(last == -1 && first == -1) {
1489       query.prepare(queryString("select_messagesNewestK"));
1490     } else if(last == -1) {
1491       query.prepare(queryString("select_messagesNewerThan"));
1492       query.bindValue(":firstmsg", first.toInt());
1493     } else {
1494       query.prepare(queryString("select_messages"));
1495       query.bindValue(":lastmsg", last.toInt());
1496       query.bindValue(":firstmsg", first.toInt());
1497     }
1498     query.bindValue(":bufferid", bufferId.toInt());
1499     query.bindValue(":limit", limit);
1500
1501     safeExec(query);
1502     watchQuery(query);
1503
1504     while(query.next()) {
1505       Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
1506                   bufferInfo,
1507                   (Message::Type)query.value(2).toUInt(),
1508                   query.value(5).toString(),
1509                   query.value(4).toString(),
1510                   (Message::Flags)query.value(3).toUInt());
1511       msg.setMsgId(query.value(0).toInt());
1512       messagelist << msg;
1513     }
1514   }
1515   db.commit();
1516   unlock();
1517
1518   return messagelist;
1519 }
1520
1521 QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) {
1522   QList<Message> messagelist;
1523
1524   QSqlDatabase db = logDb();
1525   db.transaction();
1526
1527   QHash<BufferId, BufferInfo> bufferInfoHash;
1528   {
1529     QSqlQuery bufferInfoQuery(db);
1530     bufferInfoQuery.prepare(queryString("select_buffers"));
1531     bufferInfoQuery.bindValue(":userid", user.toInt());
1532
1533     lockForRead();
1534     safeExec(bufferInfoQuery);
1535     watchQuery(bufferInfoQuery);
1536     while(bufferInfoQuery.next()) {
1537       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());
1538       bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
1539     }
1540
1541     QSqlQuery query(db);
1542     if(last == -1) {
1543       query.prepare(queryString("select_messagesAllNew"));
1544     } else {
1545       query.prepare(queryString("select_messagesAll"));
1546       query.bindValue(":lastmsg", last.toInt());
1547     }
1548     query.bindValue(":userid", user.toInt());
1549     query.bindValue(":firstmsg", first.toInt());
1550     query.bindValue(":limit", limit);
1551     safeExec(query);
1552
1553     watchQuery(query);
1554
1555     while(query.next()) {
1556       Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
1557                   bufferInfoHash[query.value(1).toInt()],
1558                   (Message::Type)query.value(3).toUInt(),
1559                   query.value(6).toString(),
1560                   query.value(5).toString(),
1561                   (Message::Flags)query.value(4).toUInt());
1562       msg.setMsgId(query.value(0).toInt());
1563       messagelist << msg;
1564     }
1565   }
1566   db.commit();
1567   unlock();
1568   return messagelist;
1569 }
1570
1571 QString SqliteStorage::backlogFile() {
1572   return Quassel::configDirPath() + "quassel-storage.sqlite";
1573 }
1574
1575 bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount) {
1576   query.exec();
1577
1578   if(!query.lastError().isValid())
1579     return true;
1580
1581   switch(query.lastError().number()) {
1582   case 5: // SQLITE_BUSY         5   /* The database file is locked */
1583   case 6: // SQLITE_LOCKED       6   /* A table in the database is locked */
1584     if(retryCount < _maxRetryCount)
1585       return safeExec(query, retryCount + 1);
1586   default:
1587     return false;
1588   }
1589 }
1590
1591
1592 // ========================================
1593 //  SqliteMigration
1594 // ========================================
1595 SqliteMigrationReader::SqliteMigrationReader()
1596   : SqliteStorage(),
1597     _maxId(0)
1598 {
1599 }
1600
1601 void SqliteMigrationReader::setMaxId(MigrationObject mo) {
1602   QString queryString;
1603   switch(mo) {
1604   case Sender:
1605     queryString = "SELECT max(senderid) FROM sender";
1606     break;
1607   case Backlog:
1608     queryString = "SELECT max(messageid) FROM backlog";
1609     break;
1610   default:
1611     _maxId = 0;
1612     return;
1613   }
1614   QSqlQuery query = logDb().exec(queryString);
1615   query.first();
1616   _maxId = query.value(0).toInt();
1617 }
1618
1619 bool SqliteMigrationReader::prepareQuery(MigrationObject mo) {
1620   setMaxId(mo);
1621
1622   switch(mo) {
1623   case QuasselUser:
1624     newQuery(queryString("migrate_read_quasseluser"), logDb());
1625     break;
1626   case Identity:
1627     newQuery(queryString("migrate_read_identity"), logDb());
1628     break;
1629   case IdentityNick:
1630     newQuery(queryString("migrate_read_identity_nick"), logDb());
1631     break;
1632   case Network:
1633     newQuery(queryString("migrate_read_network"), logDb());
1634     break;
1635   case Buffer:
1636     newQuery(queryString("migrate_read_buffer"), logDb());
1637     break;
1638   case Sender:
1639     newQuery(queryString("migrate_read_sender"), logDb());
1640     bindValue(0, 0);
1641     bindValue(1, stepSize());
1642     break;
1643   case Backlog:
1644     newQuery(queryString("migrate_read_backlog"), logDb());
1645     bindValue(0, 0);
1646     bindValue(1, stepSize());
1647     break;
1648   case IrcServer:
1649     newQuery(queryString("migrate_read_ircserver"), logDb());
1650     break;
1651   case UserSetting:
1652     newQuery(queryString("migrate_read_usersetting"), logDb());
1653     break;
1654   }
1655   return exec();
1656 }
1657
1658 bool SqliteMigrationReader::readMo(QuasselUserMO &user) {
1659   if(!next())
1660     return false;
1661
1662   user.id = value(0).toInt();
1663   user.username = value(1).toString();
1664   user.password = value(2).toString();
1665   return true;
1666 }
1667
1668 bool SqliteMigrationReader::readMo(IdentityMO &identity) {
1669   if(!next())
1670     return false;
1671
1672   identity.id = value(0).toInt();
1673   identity.userid = value(1).toInt();
1674   identity.identityname = value(2).toString();
1675   identity.realname = value(3).toString();
1676   identity.awayNick = value(4).toString();
1677   identity.awayNickEnabled = value(5).toInt() == 1 ? true : false;
1678   identity.awayReason = value(6).toString();
1679   identity.awayReasonEnabled = value(7).toInt() == 1 ? true : false;
1680   identity.autoAwayEnabled = value(8).toInt() == 1 ? true : false;
1681   identity.autoAwayTime = value(9).toInt();
1682   identity.autoAwayReason = value(10).toString();
1683   identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
1684   identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
1685   identity.detachAwayReason = value(13).toString();
1686   identity.detchAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
1687   identity.ident = value(15).toString();
1688   identity.kickReason = value(16).toString();
1689   identity.partReason = value(17).toString();
1690   identity.quitReason = value(18).toString();
1691   identity.sslCert = value(19).toByteArray();
1692   identity.sslKey = value(20).toByteArray();
1693   return true;
1694 }
1695
1696 bool SqliteMigrationReader::readMo(IdentityNickMO &identityNick) {
1697   if(!next())
1698     return false;
1699
1700   identityNick.nickid = value(0).toInt();
1701   identityNick.identityId = value(1).toInt();
1702   identityNick.nick = value(2).toString();
1703   return true;
1704 }
1705
1706 bool SqliteMigrationReader::readMo(NetworkMO &network) {
1707   if(!next())
1708     return false;
1709
1710   network.networkid = value(0).toInt();
1711   network.userid = value(1).toInt();
1712   network.networkname = value(2).toString();
1713   network.identityid = value(3).toInt();
1714   network.encodingcodec = value(4).toString();
1715   network.decodingcodec = value(5).toString();
1716   network.servercodec = value(6).toString();
1717   network.userandomserver = value(7).toInt() == 1 ? true : false;
1718   network.perform = value(8).toString();
1719   network.useautoidentify = value(9).toInt() == 1 ? true : false;
1720   network.autoidentifyservice = value(10).toString();
1721   network.autoidentifypassword = value(11).toString();
1722   network.useautoreconnect = value(12).toInt() == 1 ? true : false;
1723   network.autoreconnectinterval = value(13).toInt();
1724   network.autoreconnectretries = value(14).toInt();
1725   network.unlimitedconnectretries = value(15).toInt() == 1 ? true : false;
1726   network.rejoinchannels = value(16).toInt() == 1 ? true : false;
1727   network.connected = value(17).toInt() == 1 ? true : false;
1728   network.usermode = value(18).toString();
1729   network.awaymessage = value(19).toString();
1730   network.attachperform = value(20).toString();
1731   network.detachperform = value(21).toString();
1732   return true;
1733 }
1734
1735 bool SqliteMigrationReader::readMo(BufferMO &buffer) {
1736   if(!next())
1737     return false;
1738
1739   buffer.bufferid = value(0).toInt();
1740   buffer.userid = value(1).toInt();
1741   buffer.groupid = value(2).toInt();
1742   buffer.networkid = value(3).toInt();
1743   buffer.buffername = value(4).toString();
1744   buffer.buffercname = value(5).toString();
1745   buffer.buffertype = value(6).toInt();
1746   buffer.lastseenmsgid = value(7).toInt();
1747   buffer.markerlinemsgid = value(8).toInt();
1748   buffer.key = value(8).toString();
1749   buffer.joined = value(9).toInt() == 1 ? true : false;
1750   return true;
1751 }
1752
1753 bool SqliteMigrationReader::readMo(SenderMO &sender) {
1754   int skipSteps = 0;
1755   while(!next()) {
1756     if(sender.senderId < _maxId) {
1757       bindValue(0, sender.senderId + (skipSteps * stepSize()));
1758       bindValue(1, sender.senderId + ((skipSteps + 1) * stepSize()));
1759       skipSteps++;
1760       if(!exec())
1761         return false;
1762     } else {
1763       return false;
1764     }
1765   }
1766
1767   sender.senderId = value(0).toInt();
1768   sender.sender = value(1).toString();
1769   return true;
1770 }
1771
1772 bool SqliteMigrationReader::readMo(BacklogMO &backlog) {
1773   int skipSteps = 0;
1774   while(!next()) {
1775     if(backlog.messageid < _maxId) {
1776       bindValue(0, backlog.messageid.toInt() + (skipSteps * stepSize()));
1777       bindValue(1, backlog.messageid.toInt() + ((skipSteps + 1) * stepSize()));
1778       skipSteps++;
1779       if(!exec())
1780         return false;
1781     } else {
1782       return false;
1783     }
1784   }
1785
1786   backlog.messageid = value(0).toInt();
1787   backlog.time = QDateTime::fromTime_t(value(1).toInt()).toUTC();
1788   backlog.bufferid = value(2).toInt();
1789   backlog.type = value(3).toInt();
1790   backlog.flags = value(4).toInt();
1791   backlog.senderid = value(5).toInt();
1792   backlog.message = value(6).toString();
1793   return true;
1794 }
1795
1796 bool SqliteMigrationReader::readMo(IrcServerMO &ircserver) {
1797   if(!next())
1798     return false;
1799
1800   ircserver.serverid = value(0).toInt();
1801   ircserver.userid = value(1).toInt();
1802   ircserver.networkid = value(2).toInt();
1803   ircserver.hostname = value(3).toString();
1804   ircserver.port = value(4).toInt();
1805   ircserver.password = value(5).toString();
1806   ircserver.ssl = value(6).toInt() == 1 ? true : false;
1807   ircserver.sslversion = value(7).toInt();
1808   ircserver.useproxy = value(8).toInt() == 1 ? true : false;
1809   ircserver.proxytype = value(9).toInt();
1810   ircserver.proxyhost = value(10).toString();
1811   ircserver.proxyport = value(11).toInt();
1812   ircserver.proxyuser = value(12).toString();
1813   ircserver.proxypass = value(13).toString();
1814   return true;
1815 }
1816
1817 bool SqliteMigrationReader::readMo(UserSettingMO &userSetting) {
1818   if(!next())
1819     return false;
1820
1821   userSetting.userid = value(0).toInt();
1822   userSetting.settingname = value(1).toString();
1823   userSetting.settingvalue = value(2).toByteArray();
1824
1825   return true;
1826 }