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