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