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