Well, if we check for a settings version, maybe we should set it too...
[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; // yes this is a large number... only other way to "handle" this is bailing out...
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   QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
56   if(query.first())
57     return query.value(0).toInt();
58
59   // maybe it's really old... (schema version 0)
60   query = logDb().exec("SELECT MAX(version) FROM coreinfo");
61   if(query.first())
62     return query.value(0).toInt();
63
64   return AbstractSqlStorage::installedSchemaVersion();
65 }
66
67 UserId SqliteStorage::addUser(const QString &user, const QString &password) {
68   QSqlQuery query(logDb());
69   query.prepare(queryString("insert_quasseluser"));
70   query.bindValue(":username", user);
71   query.bindValue(":password", cryptedPassword(password));
72   safeExec(query);
73   if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
74     return 0;
75   }
76
77   query.prepare(queryString("select_userid"));
78   query.bindValue(":username", user);
79   safeExec(query);
80   query.first();
81   UserId uid = query.value(0).toInt();
82   emit userAdded(uid, user);
83   return uid;
84 }
85
86 void SqliteStorage::updateUser(UserId user, const QString &password) {
87   QSqlQuery query(logDb());
88   query.prepare(queryString("update_userpassword"));
89   query.bindValue(":userid", user.toInt());
90   query.bindValue(":password", cryptedPassword(password));
91   safeExec(query);
92 }
93
94 void SqliteStorage::renameUser(UserId user, const QString &newName) {
95   QSqlQuery query(logDb());
96   query.prepare(queryString("update_username"));
97   query.bindValue(":userid", user.toInt());
98   query.bindValue(":username", newName);
99   safeExec(query);
100   emit userRenamed(user, newName);
101 }
102
103 UserId SqliteStorage::validateUser(const QString &user, const QString &password) {
104   QSqlQuery query(logDb());
105   query.prepare(queryString("select_authuser"));
106   query.bindValue(":username", user);
107   query.bindValue(":password", cryptedPassword(password));
108   safeExec(query);
109
110   if(query.first()) {
111     return query.value(0).toInt();
112   } else {
113     return 0;
114   }
115 }
116
117 UserId SqliteStorage::internalUser() {
118   QSqlQuery query(logDb());
119   query.prepare(queryString("select_internaluser"));
120   safeExec(query);
121
122   if(query.first()) {
123     return query.value(0).toInt();
124   } else {
125     return 0;
126   }
127 }
128
129 void SqliteStorage::delUser(UserId user) {
130   QSqlQuery query(logDb());
131   query.prepare(queryString("delete_backlog_by_uid"));
132   query.bindValue(":userid", user.toInt());
133   safeExec(query);
134
135   query.prepare(queryString("delete_buffers_by_uid"));
136   query.bindValue(":userid", user.toInt());
137   safeExec(query);
138
139   query.prepare(queryString("delete_networks_by_uid"));
140   query.bindValue(":userid", user.toInt());
141   safeExec(query);
142
143   query.prepare(queryString("delete_quasseluser"));
144   query.bindValue(":userid", user.toInt());
145   safeExec(query);
146   // I hate the lack of foreign keys and on delete cascade... :(
147   emit userRemoved(user);
148 }
149
150 void SqliteStorage::setUserSetting(UserId userId, const QString &settingName, const QVariant &data) {
151   QByteArray rawData;
152   QDataStream out(&rawData, QIODevice::WriteOnly);
153   out.setVersion(QDataStream::Qt_4_2);
154   out << data;
155
156   QSqlQuery query(logDb());
157   query.prepare(queryString("insert_user_setting"));
158   query.bindValue(":userid", userId.toInt());
159   query.bindValue(":settingname", settingName);
160   query.bindValue(":settingvalue", rawData);
161   safeExec(query);
162
163   if(query.lastError().isValid()) {
164     QSqlQuery updateQuery(logDb());
165     updateQuery.prepare(queryString("update_user_setting"));
166     updateQuery.bindValue(":userid", userId.toInt());
167     updateQuery.bindValue(":settingname", settingName);
168     updateQuery.bindValue(":settingvalue", rawData);
169     safeExec(updateQuery);
170   }
171
172 }
173
174 QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData) {
175   QSqlQuery query(logDb());
176   query.prepare(queryString("select_user_setting"));
177   query.bindValue(":userid", userId.toInt());
178   query.bindValue(":settingname", settingName);
179   safeExec(query);
180
181   if(query.first()) {
182     QVariant data;
183     QByteArray rawData = query.value(0).toByteArray();
184     QDataStream in(&rawData, QIODevice::ReadOnly);
185     in.setVersion(QDataStream::Qt_4_2);
186     in >> data;
187     return data;
188   } else {
189     return defaultData;
190   }
191 }
192
193 IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity) {
194   IdentityId identityId;
195
196   QSqlQuery query(logDb());
197   query.prepare(queryString("insert_identity"));
198   query.bindValue(":userid", user.toInt());
199   query.bindValue(":identityname", identity.identityName());
200   query.bindValue(":realname", identity.realName());
201   query.bindValue(":awaynick", identity.awayNick());
202   query.bindValue(":awaynickenabled", identity.awayNickEnabled() ? 1 : 0);
203   query.bindValue(":awayreason", identity.awayReason());
204   query.bindValue(":awayreasonenabled", identity.awayReasonEnabled() ? 1 : 0);
205   query.bindValue(":autoawayenabled", identity.awayReasonEnabled() ? 1 : 0);
206   query.bindValue(":autoawaytime", identity.autoAwayTime());
207   query.bindValue(":autoawayreason", identity.autoAwayReason());
208   query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled() ? 1 : 0);
209   query.bindValue(":detachawayenabled", identity.detachAwayEnabled() ? 1 : 0);
210   query.bindValue(":detachawayreason", identity.detachAwayReason());
211   query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled() ? 1 : 0);
212   query.bindValue(":ident", identity.ident());
213   query.bindValue(":kickreason", identity.kickReason());
214   query.bindValue(":partreason", identity.partReason());
215   query.bindValue(":quitreason", identity.quitReason());
216 #ifdef HAVE_SSL
217   query.bindValue(":sslcert", identity.sslCert().toPem());
218   query.bindValue(":sslkey", identity.sslKey().toPem());
219 #else
220   query.bindValue(":sslcert", QByteArray());
221   query.bindValue(":sslkey", QByteArray());
222 #endif
223
224   safeExec(query);
225
226   identityId = query.lastInsertId().toInt();
227   if(!identityId.isValid()) {
228     watchQuery(query);
229   } else {
230     QSqlQuery deleteNickQuery(logDb());
231     deleteNickQuery.prepare(queryString("delete_nicks"));
232     deleteNickQuery.bindValue(":identityid", identityId.toInt());
233     safeExec(deleteNickQuery);
234
235     QSqlQuery insertNickQuery(logDb());
236     insertNickQuery.prepare(queryString("insert_nick"));
237     foreach(QString nick, identity.nicks()) {
238       insertNickQuery.bindValue(":identityid", identityId.toInt());
239       insertNickQuery.bindValue(":nick", nick);
240       safeExec(insertNickQuery);
241     }
242   }
243   identity.setId(identityId);
244   return identityId;
245 }
246
247 bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity) {
248   QSqlQuery checkQuery(logDb());
249   checkQuery.prepare(queryString("select_checkidentity"));
250   checkQuery.bindValue(":identityid", identity.id().toInt());
251   checkQuery.bindValue(":userid", user.toInt());
252   safeExec(checkQuery);
253
254   if(!checkQuery.first() || checkQuery.value(0).toInt() != 1) // there should be exactly one identity for the given id and user
255     return false;
256
257   QSqlQuery query(logDb());
258   query.prepare(queryString("update_identity"));
259   query.bindValue(":identityname", identity.identityName());
260   query.bindValue(":realname", identity.realName());
261   query.bindValue(":awaynick", identity.awayNick());
262   query.bindValue(":awaynickenabled", identity.awayNickEnabled() ? 1 : 0);
263   query.bindValue(":awayreason", identity.awayReason());
264   query.bindValue(":awayreasonenabled", identity.awayReasonEnabled() ? 1 : 0);
265   query.bindValue(":autoawayenabled", identity.awayReasonEnabled() ? 1 : 0);
266   query.bindValue(":autoawaytime", identity.autoAwayTime());
267   query.bindValue(":autoawayreason", identity.autoAwayReason());
268   query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled() ? 1 : 0);
269   query.bindValue(":detachawayenabled", identity.detachAwayEnabled() ? 1 : 0);
270   query.bindValue(":detachawayreason", identity.detachAwayReason());
271   query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled() ? 1 : 0);
272   query.bindValue(":ident", identity.ident());
273   query.bindValue(":kickreason", identity.kickReason());
274   query.bindValue(":partreason", identity.partReason());
275   query.bindValue(":quitreason", identity.quitReason());
276 #ifdef HAVE_SSL
277   query.bindValue(":sslcert", identity.sslCert().toPem());
278   query.bindValue(":sslkey", identity.sslKey().toPem());
279 #else
280   query.bindValue(":sslcert", QByteArray());
281   query.bindValue(":sslkey", QByteArray());
282 #endif
283   query.bindValue(":identityid", identity.id().toInt());
284   safeExec(query);
285   watchQuery(query);
286
287   QSqlQuery deleteNickQuery(logDb());
288   deleteNickQuery.prepare(queryString("delete_nicks"));
289   deleteNickQuery.bindValue(":identityid", identity.id().toInt());
290   safeExec(deleteNickQuery);
291   watchQuery(deleteNickQuery);
292
293   QSqlQuery insertNickQuery(logDb());
294   insertNickQuery.prepare(queryString("insert_nick"));
295   foreach(QString nick, identity.nicks()) {
296     insertNickQuery.bindValue(":identityid", identity.id().toInt());
297     insertNickQuery.bindValue(":nick", nick);
298     safeExec(insertNickQuery);
299     watchQuery(insertNickQuery);
300   }
301
302   return true;
303 }
304
305 void SqliteStorage::removeIdentity(UserId user, IdentityId identityId) {
306   QSqlQuery checkQuery(logDb());
307   checkQuery.prepare(queryString("select_checkidentity"));
308   checkQuery.bindValue(":identityid", identityId.toInt());
309   checkQuery.bindValue(":userid", user.toInt());
310   safeExec(checkQuery);
311
312   if(!checkQuery.first() || checkQuery.value(0).toInt() != 1) // there should be exactly one identity for the given id and user
313     return;
314
315   QSqlQuery deleteNickQuery(logDb());
316   deleteNickQuery.prepare(queryString("delete_nicks"));
317   deleteNickQuery.bindValue(":identityid", identityId.toInt());
318   safeExec(deleteNickQuery);
319
320   QSqlQuery deleteIdentityQuery(logDb());
321   deleteIdentityQuery.prepare(queryString("delete_identity"));
322   deleteIdentityQuery.bindValue(":identityid", identityId.toInt());
323   deleteIdentityQuery.bindValue(":userid", user.toInt());
324   safeExec(deleteIdentityQuery);
325 }
326
327 QList<CoreIdentity> SqliteStorage::identities(UserId user) {
328   QList<CoreIdentity> identities;
329
330   QSqlQuery query(logDb());
331   query.prepare(queryString("select_identities"));
332   query.bindValue(":userid", user.toInt());
333
334   QSqlQuery nickQuery(logDb());
335   nickQuery.prepare(queryString("select_nicks"));
336
337   safeExec(query);
338
339   while(query.next()) {
340     CoreIdentity identity(IdentityId(query.value(0).toInt()));
341
342     identity.setIdentityName(query.value(1).toString());
343     identity.setRealName(query.value(2).toString());
344     identity.setAwayNick(query.value(3).toString());
345     identity.setAwayNickEnabled(!!query.value(4).toInt());
346     identity.setAwayReason(query.value(5).toString());
347     identity.setAwayReasonEnabled(!!query.value(6).toInt());
348     identity.setAutoAwayEnabled(!!query.value(7).toInt());
349     identity.setAutoAwayTime(query.value(8).toInt());
350     identity.setAutoAwayReason(query.value(9).toString());
351     identity.setAutoAwayReasonEnabled(!!query.value(10).toInt());
352     identity.setDetachAwayEnabled(!!query.value(11).toInt());
353     identity.setDetachAwayReason(query.value(12).toString());
354     identity.setDetachAwayReasonEnabled(!!query.value(13).toInt());
355     identity.setIdent(query.value(14).toString());
356     identity.setKickReason(query.value(15).toString());
357     identity.setPartReason(query.value(16).toString());
358     identity.setQuitReason(query.value(17).toString());
359 #ifdef HAVE_SSL
360     identity.setSslCert(query.value(18).toByteArray());
361     identity.setSslKey(query.value(19).toByteArray());
362 #endif
363
364     nickQuery.bindValue(":identityid", identity.id().toInt());
365     QList<QString> nicks;
366     safeExec(nickQuery);
367     watchQuery(nickQuery);
368     while(nickQuery.next()) {
369       nicks << nickQuery.value(0).toString();
370     }
371     identity.setNicks(nicks);
372     identities << identity;
373   }
374   return identities;
375 }
376
377 NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info) {
378   NetworkId networkId;
379   QSqlQuery query(logDb());
380   query.prepare(queryString("insert_network"));
381   query.bindValue(":userid", user.toInt());
382   query.bindValue(":networkname", info.networkName);
383   safeExec(query);
384
385   networkId = getNetworkId(user, info.networkName);
386   if(!networkId.isValid()) {
387     watchQuery(query);
388   } else {
389     NetworkInfo newNetworkInfo = info;
390     newNetworkInfo.networkId = networkId;
391     updateNetwork(user, newNetworkInfo);
392   }
393   return networkId;
394 }
395
396 bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info) {
397   if(!isValidNetwork(user, info.networkId))
398      return false;
399
400   QSqlQuery updateQuery(logDb());
401   updateQuery.prepare(queryString("update_network"));
402   updateQuery.bindValue(":networkname", info.networkName);
403   updateQuery.bindValue(":identityid", info.identity.toInt());
404   updateQuery.bindValue(":usecustomencoding", info.useCustomEncodings ? 1 : 0);
405   updateQuery.bindValue(":encodingcodec", QString(info.codecForEncoding));
406   updateQuery.bindValue(":decodingcodec", QString(info.codecForDecoding));
407   updateQuery.bindValue(":servercodec", QString(info.codecForServer));
408   updateQuery.bindValue(":userandomserver", info.useRandomServer ? 1 : 0);
409   updateQuery.bindValue(":perform", info.perform.join("\n"));
410   updateQuery.bindValue(":useautoidentify", info.useAutoIdentify ? 1 : 0);
411   updateQuery.bindValue(":autoidentifyservice", info.autoIdentifyService);
412   updateQuery.bindValue(":autoidentifypassword", info.autoIdentifyPassword);
413   updateQuery.bindValue(":useautoreconnect", info.useAutoReconnect ? 1 : 0);
414   updateQuery.bindValue(":autoreconnectinterval", info.autoReconnectInterval);
415   updateQuery.bindValue(":autoreconnectretries", info.autoReconnectRetries);
416   updateQuery.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0);
417   updateQuery.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0);
418   updateQuery.bindValue(":networkid", info.networkId.toInt());
419   safeExec(updateQuery);
420   if(!watchQuery(updateQuery))
421     return false;
422
423   QSqlQuery dropServersQuery(logDb());
424   dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
425   dropServersQuery.bindValue(":networkid", info.networkId.toInt());
426   safeExec(dropServersQuery);
427   if(!watchQuery(dropServersQuery))
428     return false;
429
430   QSqlQuery insertServersQuery(logDb());
431   insertServersQuery.prepare(queryString("insert_server"));
432   foreach(Network::Server server, info.serverList) {
433     insertServersQuery.bindValue(":userid", user.toInt());
434     insertServersQuery.bindValue(":networkid", info.networkId.toInt());
435     insertServersQuery.bindValue(":hostname", server.host);
436     insertServersQuery.bindValue(":port", server.port);
437     insertServersQuery.bindValue(":password", server.password);
438     insertServersQuery.bindValue(":ssl", server.useSsl ? 1 : 0);
439     insertServersQuery.bindValue(":sslversion", server.sslVersion);
440     insertServersQuery.bindValue(":useproxy", server.useProxy ? 1 : 0);
441     insertServersQuery.bindValue(":proxytype", server.proxyType);
442     insertServersQuery.bindValue(":proxyhost", server.proxyHost);
443     insertServersQuery.bindValue(":proxyport", server.proxyPort);
444     insertServersQuery.bindValue(":proxyuser", server.proxyUser);
445     insertServersQuery.bindValue(":proxypass", server.proxyPass);
446
447     safeExec(insertServersQuery);
448     if(!watchQuery(insertServersQuery))
449       return false;
450   }
451
452   return true;
453 }
454
455 bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId) {
456   if(!isValidNetwork(user, networkId))
457      return false;
458
459   bool withTransaction = logDb().driver()->hasFeature(QSqlDriver::Transactions);
460   if(withTransaction) {
461     sync();
462     if(!logDb().transaction()) {
463       qWarning() << "SqliteStorage::removeNetwork(): cannot start transaction. continuing with out rollback support!";
464       withTransaction = false;
465     }
466   }
467
468   QSqlQuery deleteBacklogQuery(logDb());
469   deleteBacklogQuery.prepare(queryString("delete_backlog_for_network"));
470   deleteBacklogQuery.bindValue(":networkid", networkId.toInt());
471   safeExec(deleteBacklogQuery);
472   if(!watchQuery(deleteBacklogQuery)) {
473     if(withTransaction)
474       logDb().rollback();
475     return false;
476   }
477
478   QSqlQuery deleteBuffersQuery(logDb());
479   deleteBuffersQuery.prepare(queryString("delete_buffers_for_network"));
480   deleteBuffersQuery.bindValue(":networkid", networkId.toInt());
481   safeExec(deleteBuffersQuery);
482   if(!watchQuery(deleteBuffersQuery)) {
483     if(withTransaction)
484       logDb().rollback();
485     return false;
486   }
487
488   QSqlQuery deleteServersQuery(logDb());
489   deleteServersQuery.prepare(queryString("delete_ircservers_for_network"));
490   deleteServersQuery.bindValue(":networkid", networkId.toInt());
491   safeExec(deleteServersQuery);
492   if(!watchQuery(deleteServersQuery)) {
493     if(withTransaction)
494       logDb().rollback();
495     return false;
496   }
497
498   QSqlQuery deleteNetworkQuery(logDb());
499   deleteNetworkQuery.prepare(queryString("delete_network"));
500   deleteNetworkQuery.bindValue(":networkid", networkId.toInt());
501   safeExec(deleteNetworkQuery);
502   if(!watchQuery(deleteNetworkQuery)) {
503     if(withTransaction)
504       logDb().rollback();
505     return false;
506   }
507
508   logDb().commit();
509   return true;
510 }
511
512 QList<NetworkInfo> SqliteStorage::networks(UserId user) {
513   QList<NetworkInfo> nets;
514
515   QSqlQuery networksQuery(logDb());
516   networksQuery.prepare(queryString("select_networks_for_user"));
517   networksQuery.bindValue(":userid", user.toInt());
518
519   QSqlQuery serversQuery(logDb());
520   serversQuery.prepare(queryString("select_servers_for_network"));
521
522   safeExec(networksQuery);
523   if(!watchQuery(networksQuery))
524     return nets;
525
526   while(networksQuery.next()) {
527     NetworkInfo net;
528     net.networkId = networksQuery.value(0).toInt();
529     net.networkName = networksQuery.value(1).toString();
530     net.identity = networksQuery.value(2).toInt();
531     net.codecForServer = networksQuery.value(3).toString().toAscii();
532     net.codecForEncoding = networksQuery.value(4).toString().toAscii();
533     net.codecForDecoding = networksQuery.value(5).toString().toAscii();
534     net.useRandomServer = networksQuery.value(6).toInt() == 1 ? true : false;
535     net.perform = networksQuery.value(7).toString().split("\n");
536     net.useAutoIdentify = networksQuery.value(8).toInt() == 1 ? true : false;
537     net.autoIdentifyService = networksQuery.value(9).toString();
538     net.autoIdentifyPassword = networksQuery.value(10).toString();
539     net.useAutoReconnect = networksQuery.value(11).toInt() == 1 ? true : false;
540     net.autoReconnectInterval = networksQuery.value(12).toUInt();
541     net.autoReconnectRetries = networksQuery.value(13).toInt();
542     net.unlimitedReconnectRetries = networksQuery.value(14).toInt() == 1 ? true : false;
543     net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false;
544
545     serversQuery.bindValue(":networkid", net.networkId.toInt());
546     safeExec(serversQuery);
547     if(!watchQuery(serversQuery))
548       return nets;
549
550     Network::ServerList servers;
551     while(serversQuery.next()) {
552       Network::Server server;
553       server.host = serversQuery.value(0).toString();
554       server.port = serversQuery.value(1).toUInt();
555       server.password = serversQuery.value(2).toString();
556       server.useSsl = serversQuery.value(3).toInt() == 1 ? true : false;
557       server.sslVersion = serversQuery.value(4).toInt();
558       server.useProxy = serversQuery.value(5).toInt() == 1 ? true : false;
559       server.proxyType = serversQuery.value(6).toInt();
560       server.proxyHost = serversQuery.value(7).toString();
561       server.proxyPort = serversQuery.value(8).toUInt();
562       server.proxyUser = serversQuery.value(9).toString();
563       server.proxyPass = serversQuery.value(10).toString();
564       servers << server;
565     }
566     net.serverList = servers;
567     nets << net;
568   }
569   return nets;
570 }
571
572 bool SqliteStorage::isValidNetwork(UserId user, const NetworkId &networkId) {
573   QSqlQuery query(logDb());
574   query.prepare(queryString("select_networkExists"));
575   query.bindValue(":userid", user.toInt());
576   query.bindValue(":networkid", networkId.toInt());
577   safeExec(query);
578
579   watchQuery(query); // there should not occur any errors
580   if(!query.first())
581     return false;
582
583   Q_ASSERT(!query.next());
584   return true;
585 }
586
587 bool SqliteStorage::isValidBuffer(const UserId &user, const BufferId &bufferId) {
588   QSqlQuery query(logDb());
589   query.prepare(queryString("select_bufferExists"));
590   query.bindValue(":userid", user.toInt());
591   query.bindValue(":bufferid", bufferId.toInt());
592   safeExec(query);
593
594   watchQuery(query);
595   if(!query.first())
596     return false;
597
598   Q_ASSERT(!query.next());
599   return true;
600 }
601
602 NetworkId SqliteStorage::getNetworkId(UserId user, const QString &network) {
603   QSqlQuery query(logDb());
604   query.prepare("SELECT networkid FROM network "
605                 "WHERE userid = :userid AND networkname = :networkname");
606   query.bindValue(":userid", user.toInt());
607   query.bindValue(":networkname", network);
608   safeExec(query);
609
610   if(query.first())
611     return query.value(0).toInt();
612   else
613     return NetworkId();
614 }
615
616 QList<NetworkId> SqliteStorage::connectedNetworks(UserId user) {
617   QList<NetworkId> connectedNets;
618   QSqlQuery query(logDb());
619   query.prepare(queryString("select_connected_networks"));
620   query.bindValue(":userid", user.toInt());
621   safeExec(query);
622   watchQuery(query);
623
624   while(query.next()) {
625     connectedNets << query.value(0).toInt();
626   }
627
628   return connectedNets;
629 }
630
631 void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected) {
632   QSqlQuery query(logDb());
633   query.prepare(queryString("update_network_connected"));
634   query.bindValue(":userid", user.toInt());
635   query.bindValue(":networkid", networkId.toInt());
636   query.bindValue(":connected", isConnected ? 1 : 0);
637   safeExec(query);
638   watchQuery(query);
639 }
640
641 QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId &networkId) {
642   QHash<QString, QString> persistentChans;
643   QSqlQuery query(logDb());
644   query.prepare(queryString("select_persistent_channels"));
645   query.bindValue(":userid", user.toInt());
646   query.bindValue(":networkid", networkId.toInt());
647   safeExec(query);
648   watchQuery(query);
649
650   while(query.next()) {
651     persistentChans[query.value(0).toString()] = query.value(1).toString();
652   }
653
654   return persistentChans;
655 }
656
657 void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined) {
658   QSqlQuery query(logDb());
659   query.prepare(queryString("update_buffer_persistent_channel"));
660   query.bindValue(":userid", user.toInt());
661   query.bindValue(":networkId", networkId.toInt());
662   query.bindValue(":buffercname", channel.toLower());
663   query.bindValue(":joined", isJoined ? 1 : 0);
664   safeExec(query);
665   watchQuery(query);
666 }
667
668 void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key) {
669   QSqlQuery query(logDb());
670   query.prepare(queryString("update_buffer_set_channel_key"));
671   query.bindValue(":userid", user.toInt());
672   query.bindValue(":networkId", networkId.toInt());
673   query.bindValue(":buffercname", channel.toLower());
674   query.bindValue(":key", key);
675   safeExec(query);
676   watchQuery(query);
677 }
678
679
680 void SqliteStorage::createBuffer(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
681   QSqlQuery query(logDb());
682   query.prepare(queryString("insert_buffer"));
683   query.bindValue(":userid", user.toInt());
684   query.bindValue(":networkid", networkId.toInt());
685   query.bindValue(":buffertype", (int)type);
686   query.bindValue(":buffername", buffer);
687   query.bindValue(":buffercname", buffer.toLower());
688   safeExec(query);
689
690   watchQuery(query);
691 }
692
693 BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer, bool create) {
694   QSqlQuery query(logDb());
695   query.prepare(queryString("select_bufferByName"));
696   query.bindValue(":networkid", networkId.toInt());
697   query.bindValue(":userid", user.toInt());
698   query.bindValue(":buffercname", buffer.toLower());
699   safeExec(query);
700
701   if(!query.first()) {
702     if(!create)
703       return BufferInfo();
704
705     createBuffer(user, networkId, type, buffer);
706     safeExec(query);
707     if(!query.first()) {
708       watchQuery(query);
709       qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
710       return BufferInfo();
711     }
712   }
713
714   BufferInfo bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
715   if(query.next()) {
716     qCritical() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
717     qCritical() << "         Query:" << query.lastQuery();
718     qCritical() << "  bound Values:";
719     QList<QVariant> list = query.boundValues().values();
720     for (int i = 0; i < list.size(); ++i)
721       qCritical() << i << ":" << list.at(i).toString().toAscii().data();
722     Q_ASSERT(false);
723   }
724
725   return bufferInfo;
726 }
727
728 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId) {
729   QSqlQuery query(logDb());
730   query.prepare(queryString("select_buffer_by_id"));
731   query.bindValue(":userid", user.toInt());
732   query.bindValue(":bufferid", bufferId.toInt());
733   safeExec(query);
734   if(!watchQuery(query))
735     return BufferInfo();
736
737   if(!query.first())
738     return BufferInfo();
739
740   BufferInfo bufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
741   Q_ASSERT(!query.next());
742
743   return bufferInfo;
744 }
745
746 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user) {
747   QList<BufferInfo> bufferlist;
748   QSqlQuery query(logDb());
749   query.prepare(queryString("select_buffers"));
750   query.bindValue(":userid", user.toInt());
751
752   safeExec(query);
753   watchQuery(query);
754   while(query.next()) {
755     bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
756   }
757   return bufferlist;
758 }
759
760 QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) {
761   QList<BufferId> bufferList;
762   QSqlQuery query(logDb());
763   query.prepare(queryString("select_buffers_for_network"));
764   query.bindValue(":networkid", networkId.toInt());
765   query.bindValue(":userid", user.toInt());
766
767   safeExec(query);
768   watchQuery(query);
769   while(query.next()) {
770     bufferList << BufferId(query.value(0).toInt());
771   }
772   return bufferList;
773 }
774
775 bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
776   if(!isValidBuffer(user, bufferId))
777     return false;
778
779   QSqlQuery delBacklogQuery(logDb());
780   delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
781   delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
782   safeExec(delBacklogQuery);
783   if(!watchQuery(delBacklogQuery))
784     return false;
785
786   QSqlQuery delBufferQuery(logDb());
787   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
788   delBufferQuery.bindValue(":bufferid", bufferId.toInt());
789   safeExec(delBufferQuery);
790   if(!watchQuery(delBufferQuery))
791     return false;
792
793   return true;
794 }
795
796 bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName) {
797   if(!isValidBuffer(user, bufferId))
798     return false;
799
800   QSqlQuery query(logDb());
801   query.prepare(queryString("update_buffer_name"));
802   query.bindValue(":buffername", newName);
803   query.bindValue(":buffercname", newName.toLower());
804   query.bindValue(":bufferid", bufferId.toInt());
805   safeExec(query);
806   if(query.lastError().isValid()) {
807     // unexepcted error occured (19 == constraint violation)
808     if(query.lastError().number() != 19)
809       watchQuery(query);
810     return false;
811   }
812
813   return true;
814 }
815
816 bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
817   if(!isValidBuffer(user, bufferId1) || !isValidBuffer(user, bufferId2))
818     return false;
819
820   QSqlQuery query(logDb());
821   query.prepare(queryString("update_backlog_bufferid"));
822   query.bindValue(":oldbufferid", bufferId2.toInt());
823   query.bindValue(":newbufferid", bufferId1.toInt());
824   safeExec(query);
825   if(!watchQuery(query))
826     return false;
827
828   QSqlQuery delBufferQuery(logDb());
829   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
830   delBufferQuery.bindValue(":bufferid", bufferId2.toInt());
831   safeExec(delBufferQuery);
832   watchQuery(delBufferQuery);
833
834   return true;
835 }
836
837 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
838   QSqlQuery query(logDb());
839   query.prepare(queryString("update_buffer_lastseen"));
840
841   query.bindValue(":userid", user.toInt());
842   query.bindValue(":bufferid", bufferId.toInt());
843   query.bindValue(":lastseenmsgid", msgId.toInt());
844   safeExec(query);
845   watchQuery(query);
846 }
847
848 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user) {
849   QHash<BufferId, MsgId> lastSeenHash;
850   QSqlQuery query(logDb());
851   query.prepare(queryString("select_buffer_lastseen_messages"));
852   query.bindValue(":userid", user.toInt());
853   safeExec(query);
854   if(!watchQuery(query))
855     return lastSeenHash;
856
857   while(query.next()) {
858     lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
859   }
860   return lastSeenHash;
861 }
862
863 MsgId SqliteStorage::logMessage(Message msg) {
864   QSqlQuery logMessageQuery(logDb());
865   logMessageQuery.prepare(queryString("insert_message"));
866
867   logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
868   logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
869   logMessageQuery.bindValue(":type", msg.type());
870   logMessageQuery.bindValue(":flags", (int)msg.flags());
871   logMessageQuery.bindValue(":sender", msg.sender());
872   logMessageQuery.bindValue(":message", msg.contents());
873   safeExec(logMessageQuery);
874
875   if(logMessageQuery.lastError().isValid()) {
876     // constraint violation - must be NOT NULL constraint - probably the sender is missing...
877     if(logMessageQuery.lastError().number() == 19) {
878       QSqlQuery addSenderQuery(logDb());
879       addSenderQuery.prepare(queryString("insert_sender"));
880       addSenderQuery.bindValue(":sender", msg.sender());
881       safeExec(addSenderQuery);
882       safeExec(logMessageQuery);
883       if(!watchQuery(logMessageQuery))
884         return 0;
885     } else {
886       watchQuery(logMessageQuery);
887     }
888   }
889
890   MsgId msgId = logMessageQuery.lastInsertId().toInt();
891   Q_ASSERT(msgId.isValid());
892   return msgId;
893 }
894
895 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
896   QList<Message> messagelist;
897
898   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
899   if(!bufferInfo.isValid())
900     return messagelist;
901
902   QSqlQuery query(logDb());
903   
904   if(last == -1 && first == -1) {
905     query.prepare(queryString("select_messagesNewestK"));
906   } else if(last == -1) {
907     query.prepare(queryString("select_messagesNewerThan"));
908     query.bindValue(":firstmsg", first.toInt());
909   } else {
910     query.prepare(queryString("select_messages"));
911     query.bindValue(":lastmsg", last.toInt());
912     query.bindValue(":firstmsg", first.toInt());
913   }
914
915   query.bindValue(":bufferid", bufferId.toInt());
916   query.bindValue(":limit", limit);
917   safeExec(query);
918
919   watchQuery(query);
920
921   while(query.next()) {
922     Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
923                 bufferInfo,
924                 (Message::Type)query.value(2).toUInt(),
925                 query.value(5).toString(),
926                 query.value(4).toString(),
927                 (Message::Flags)query.value(3).toUInt());
928     msg.setMsgId(query.value(0).toInt());
929     messagelist << msg;
930   }
931   return messagelist;
932 }
933
934 QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) {
935   QList<Message> messagelist;
936
937   QHash<BufferId, BufferInfo> bufferInfoHash;
938   foreach(BufferInfo bufferInfo, requestBuffers(user)) {
939     bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
940   }
941
942   QSqlQuery query(logDb());
943   if(last == -1) {
944     query.prepare(queryString("select_messagesAllNew"));
945   } else {
946     query.prepare(queryString("select_messagesAll"));
947     query.bindValue(":lastmsg", last.toInt());
948   }
949   query.bindValue(":userid", user.toInt());
950   query.bindValue(":firstmsg", first.toInt());
951   query.bindValue(":limit", limit);
952   safeExec(query);
953
954   watchQuery(query);
955
956   while(query.next()) {
957     Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
958                 bufferInfoHash[query.value(1).toInt()],
959                 (Message::Type)query.value(3).toUInt(),
960                 query.value(6).toString(),
961                 query.value(5).toString(),
962                 (Message::Flags)query.value(4).toUInt());
963     msg.setMsgId(query.value(0).toInt());
964     messagelist << msg;
965   }
966
967   return messagelist;
968 }
969
970 QString SqliteStorage::backlogFile() {
971   return Quassel::configDirPath() + "quassel-storage.sqlite";
972 }
973
974 bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount) {
975   query.exec();
976
977   if(!query.lastError().isValid())
978     return true;
979
980   switch(query.lastError().number()) {
981   case 5: // SQLITE_BUSY         5   /* The database file is locked */
982   case 6: // SQLITE_LOCKED       6   /* A table in the database is locked */
983     if(retryCount < _maxRetryCount)
984       return safeExec(query, retryCount + 1);
985   default:
986     return false;
987   }
988 }