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