a418d05516dcfd44ceb114feaf0c33292b964242
[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(":userid", user.toInt());
420     insertServersQuery.bindValue(":networkid", info.networkId.toInt());
421     insertServersQuery.bindValue(":hostname", server.host);
422     insertServersQuery.bindValue(":port", server.port);
423     insertServersQuery.bindValue(":password", server.password);
424     insertServersQuery.bindValue(":ssl", server.useSsl ? 1 : 0);
425     insertServersQuery.bindValue(":sslversion", server.sslVersion);
426     insertServersQuery.bindValue(":useproxy", server.useProxy ? 1 : 0);
427     insertServersQuery.bindValue(":proxytype", server.proxyType);
428     insertServersQuery.bindValue(":proxyhost", server.proxyHost);
429     insertServersQuery.bindValue(":proxyport", server.proxyPort);
430     insertServersQuery.bindValue(":proxyuser", server.proxyUser);
431     insertServersQuery.bindValue(":proxypass", server.proxyPass);
432
433     safeExec(insertServersQuery);
434     if(!watchQuery(insertServersQuery))
435       return false;
436   }
437
438   return true;
439 }
440
441 bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId) {
442   if(!isValidNetwork(user, networkId))
443      return false;
444
445   bool withTransaction = logDb().driver()->hasFeature(QSqlDriver::Transactions);
446   if(withTransaction) {
447     sync();
448     if(!logDb().transaction()) {
449       qWarning() << "SqliteStorage::removeNetwork(): cannot start transaction. continuing with out rollback support!";
450       withTransaction = false;
451     }
452   }
453
454   QSqlQuery deleteBacklogQuery(logDb());
455   deleteBacklogQuery.prepare(queryString("delete_backlog_for_network"));
456   deleteBacklogQuery.bindValue(":networkid", networkId.toInt());
457   safeExec(deleteBacklogQuery);
458   if(!watchQuery(deleteBacklogQuery)) {
459     if(withTransaction)
460       logDb().rollback();
461     return false;
462   }
463
464   QSqlQuery deleteBuffersQuery(logDb());
465   deleteBuffersQuery.prepare(queryString("delete_buffers_for_network"));
466   deleteBuffersQuery.bindValue(":networkid", networkId.toInt());
467   safeExec(deleteBuffersQuery);
468   if(!watchQuery(deleteBuffersQuery)) {
469     if(withTransaction)
470       logDb().rollback();
471     return false;
472   }
473
474   QSqlQuery deleteServersQuery(logDb());
475   deleteServersQuery.prepare(queryString("delete_ircservers_for_network"));
476   deleteServersQuery.bindValue(":networkid", networkId.toInt());
477   safeExec(deleteServersQuery);
478   if(!watchQuery(deleteServersQuery)) {
479     if(withTransaction)
480       logDb().rollback();
481     return false;
482   }
483
484   QSqlQuery deleteNetworkQuery(logDb());
485   deleteNetworkQuery.prepare(queryString("delete_network"));
486   deleteNetworkQuery.bindValue(":networkid", networkId.toInt());
487   safeExec(deleteNetworkQuery);
488   if(!watchQuery(deleteNetworkQuery)) {
489     if(withTransaction)
490       logDb().rollback();
491     return false;
492   }
493
494   logDb().commit();
495   return true;
496 }
497
498 QList<NetworkInfo> SqliteStorage::networks(UserId user) {
499   QList<NetworkInfo> nets;
500
501   QSqlQuery networksQuery(logDb());
502   networksQuery.prepare(queryString("select_networks_for_user"));
503   networksQuery.bindValue(":userid", user.toInt());
504
505   QSqlQuery serversQuery(logDb());
506   serversQuery.prepare(queryString("select_servers_for_network"));
507
508   safeExec(networksQuery);
509   if(!watchQuery(networksQuery))
510     return nets;
511
512   while(networksQuery.next()) {
513     NetworkInfo net;
514     net.networkId = networksQuery.value(0).toInt();
515     net.networkName = networksQuery.value(1).toString();
516     net.identity = networksQuery.value(2).toInt();
517     net.codecForServer = networksQuery.value(3).toString().toAscii();
518     net.codecForEncoding = networksQuery.value(4).toString().toAscii();
519     net.codecForDecoding = networksQuery.value(5).toString().toAscii();
520     net.useRandomServer = networksQuery.value(6).toInt() == 1 ? true : false;
521     net.perform = networksQuery.value(7).toString().split("\n");
522     net.useAutoIdentify = networksQuery.value(8).toInt() == 1 ? true : false;
523     net.autoIdentifyService = networksQuery.value(9).toString();
524     net.autoIdentifyPassword = networksQuery.value(10).toString();
525     net.useAutoReconnect = networksQuery.value(11).toInt() == 1 ? true : false;
526     net.autoReconnectInterval = networksQuery.value(12).toUInt();
527     net.autoReconnectRetries = networksQuery.value(13).toInt();
528     net.unlimitedReconnectRetries = networksQuery.value(14).toInt() == 1 ? true : false;
529     net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false;
530
531     serversQuery.bindValue(":networkid", net.networkId.toInt());
532     safeExec(serversQuery);
533     if(!watchQuery(serversQuery))
534       return nets;
535
536     Network::ServerList servers;
537     while(serversQuery.next()) {
538       Network::Server server;
539       server.host = serversQuery.value(0).toString();
540       server.port = serversQuery.value(1).toUInt();
541       server.password = serversQuery.value(2).toString();
542       server.useSsl = serversQuery.value(3).toInt() == 1 ? true : false;
543       server.sslVersion = serversQuery.value(4).toInt();
544       server.useProxy = serversQuery.value(5).toInt() == 1 ? true : false;
545       server.proxyType = serversQuery.value(6).toInt();
546       server.proxyHost = serversQuery.value(7).toString();
547       server.proxyPort = serversQuery.value(8).toUInt();
548       server.proxyUser = serversQuery.value(9).toString();
549       server.proxyPass = serversQuery.value(10).toString();
550       servers << server;
551     }
552     net.serverList = servers;
553     nets << net;
554   }
555   return nets;
556 }
557
558 bool SqliteStorage::isValidNetwork(UserId user, const NetworkId &networkId) {
559   QSqlQuery query(logDb());
560   query.prepare(queryString("select_networkExists"));
561   query.bindValue(":userid", user.toInt());
562   query.bindValue(":networkid", networkId.toInt());
563   safeExec(query);
564
565   watchQuery(query); // there should not occur any errors
566   if(!query.first())
567     return false;
568
569   Q_ASSERT(!query.next());
570   return true;
571 }
572
573 bool SqliteStorage::isValidBuffer(const UserId &user, const BufferId &bufferId) {
574   QSqlQuery query(logDb());
575   query.prepare(queryString("select_bufferExists"));
576   query.bindValue(":userid", user.toInt());
577   query.bindValue(":bufferid", bufferId.toInt());
578   safeExec(query);
579
580   watchQuery(query);
581   if(!query.first())
582     return false;
583
584   Q_ASSERT(!query.next());
585   return true;
586 }
587
588 NetworkId SqliteStorage::getNetworkId(UserId user, const QString &network) {
589   QSqlQuery query(logDb());
590   query.prepare("SELECT networkid FROM network "
591                 "WHERE userid = :userid AND networkname = :networkname");
592   query.bindValue(":userid", user.toInt());
593   query.bindValue(":networkname", network);
594   safeExec(query);
595
596   if(query.first())
597     return query.value(0).toInt();
598   else
599     return NetworkId();
600 }
601
602 QList<NetworkId> SqliteStorage::connectedNetworks(UserId user) {
603   QList<NetworkId> connectedNets;
604   QSqlQuery query(logDb());
605   query.prepare(queryString("select_connected_networks"));
606   query.bindValue(":userid", user.toInt());
607   safeExec(query);
608   watchQuery(query);
609
610   while(query.next()) {
611     connectedNets << query.value(0).toInt();
612   }
613
614   return connectedNets;
615 }
616
617 void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected) {
618   QSqlQuery query(logDb());
619   query.prepare(queryString("update_network_connected"));
620   query.bindValue(":userid", user.toInt());
621   query.bindValue(":networkid", networkId.toInt());
622   query.bindValue(":connected", isConnected ? 1 : 0);
623   safeExec(query);
624   watchQuery(query);
625 }
626
627 QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId &networkId) {
628   QHash<QString, QString> persistentChans;
629   QSqlQuery query(logDb());
630   query.prepare(queryString("select_persistent_channels"));
631   query.bindValue(":userid", user.toInt());
632   query.bindValue(":networkid", networkId.toInt());
633   safeExec(query);
634   watchQuery(query);
635
636   while(query.next()) {
637     persistentChans[query.value(0).toString()] = query.value(1).toString();
638   }
639
640   return persistentChans;
641 }
642
643 void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined) {
644   QSqlQuery query(logDb());
645   query.prepare(queryString("update_buffer_persistent_channel"));
646   query.bindValue(":userid", user.toInt());
647   query.bindValue(":networkId", networkId.toInt());
648   query.bindValue(":buffercname", channel.toLower());
649   query.bindValue(":joined", isJoined ? 1 : 0);
650   safeExec(query);
651   watchQuery(query);
652 }
653
654 void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key) {
655   QSqlQuery query(logDb());
656   query.prepare(queryString("update_buffer_set_channel_key"));
657   query.bindValue(":userid", user.toInt());
658   query.bindValue(":networkId", networkId.toInt());
659   query.bindValue(":buffercname", channel.toLower());
660   query.bindValue(":key", key);
661   safeExec(query);
662   watchQuery(query);
663 }
664
665
666 void SqliteStorage::createBuffer(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
667   QSqlQuery query(logDb());
668   query.prepare(queryString("insert_buffer"));
669   query.bindValue(":userid", user.toInt());
670   query.bindValue(":networkid", networkId.toInt());
671   query.bindValue(":buffertype", (int)type);
672   query.bindValue(":buffername", buffer);
673   query.bindValue(":buffercname", buffer.toLower());
674   safeExec(query);
675
676   watchQuery(query);
677 }
678
679 BufferInfo SqliteStorage::getBufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
680   QSqlQuery query(logDb());
681   query.prepare(queryString("select_bufferByName"));
682   query.bindValue(":networkid", networkId.toInt());
683   query.bindValue(":userid", user.toInt());
684   query.bindValue(":buffercname", buffer.toLower());
685   safeExec(query);
686
687   if(!query.first()) {
688     createBuffer(user, networkId, type, buffer);
689     safeExec(query);
690     if(!query.first()) {
691       watchQuery(query);
692       qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
693       return BufferInfo();
694     }
695   }
696
697   BufferInfo bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
698   if(query.next()) {
699     qCritical() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
700     qCritical() << "         Query:" << query.lastQuery();
701     qCritical() << "  bound Values:";
702     QList<QVariant> list = query.boundValues().values();
703     for (int i = 0; i < list.size(); ++i)
704       qCritical() << i << ":" << list.at(i).toString().toAscii().data();
705     Q_ASSERT(false);
706   }
707
708   return bufferInfo;
709 }
710
711 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId) {
712   QSqlQuery query(logDb());
713   query.prepare(queryString("select_buffer_by_id"));
714   query.bindValue(":userid", user.toInt());
715   query.bindValue(":bufferid", bufferId.toInt());
716   safeExec(query);
717   if(!watchQuery(query))
718     return BufferInfo();
719
720   if(!query.first())
721     return BufferInfo();
722
723   BufferInfo bufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
724   Q_ASSERT(!query.next());
725
726   return bufferInfo;
727 }
728
729 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user) {
730   QList<BufferInfo> bufferlist;
731   QSqlQuery query(logDb());
732   query.prepare(queryString("select_buffers"));
733   query.bindValue(":userid", user.toInt());
734
735   safeExec(query);
736   watchQuery(query);
737   while(query.next()) {
738     bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
739   }
740   return bufferlist;
741 }
742
743 QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) {
744   QList<BufferId> bufferList;
745   QSqlQuery query(logDb());
746   query.prepare(queryString("select_buffers_for_network"));
747   query.bindValue(":networkid", networkId.toInt());
748   query.bindValue(":userid", user.toInt());
749
750   safeExec(query);
751   watchQuery(query);
752   while(query.next()) {
753     bufferList << BufferId(query.value(0).toInt());
754   }
755   return bufferList;
756 }
757
758 bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
759   if(!isValidBuffer(user, bufferId))
760     return false;
761
762   QSqlQuery delBacklogQuery(logDb());
763   delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
764   delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
765   safeExec(delBacklogQuery);
766   if(!watchQuery(delBacklogQuery))
767     return false;
768
769   QSqlQuery delBufferQuery(logDb());
770   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
771   delBufferQuery.bindValue(":bufferid", bufferId.toInt());
772   safeExec(delBufferQuery);
773   if(!watchQuery(delBufferQuery))
774     return false;
775
776   return true;
777 }
778
779 BufferId SqliteStorage::renameBuffer(const UserId &user, const NetworkId &networkId, const QString &newName, const QString &oldName) {
780   // check if such a buffer exists...
781   QSqlQuery existsQuery(logDb());
782   existsQuery.prepare(queryString("select_bufferByName"));
783   existsQuery.bindValue(":networkid", networkId.toInt());
784   existsQuery.bindValue(":userid", user.toInt());
785   existsQuery.bindValue(":buffercname", oldName.toLower());
786   safeExec(existsQuery);
787   if(!watchQuery(existsQuery))
788     return false;
789
790   if(!existsQuery.first())
791     return false;
792
793   const int bufferid = existsQuery.value(0).toInt();
794
795   Q_ASSERT(!existsQuery.next());
796
797   // ... and if the new name is still free.
798   existsQuery.bindValue(":networkid", networkId.toInt());
799   existsQuery.bindValue(":userid", user.toInt());
800   existsQuery.bindValue(":buffercname", newName.toLower());
801   safeExec(existsQuery);
802   if(!watchQuery(existsQuery))
803     return false;
804
805   if(existsQuery.first())
806     return false;
807
808   QSqlQuery renameBufferQuery(logDb());
809   renameBufferQuery.prepare(queryString("update_buffer_name"));
810   renameBufferQuery.bindValue(":buffername", newName);
811   renameBufferQuery.bindValue(":buffercname", newName.toLower());
812   renameBufferQuery.bindValue(":bufferid", bufferid);
813   safeExec(renameBufferQuery);
814   if(watchQuery(existsQuery))
815     return BufferId(bufferid);
816   else
817     return BufferId();
818 }
819
820 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
821   QSqlQuery query(logDb());
822   query.prepare(queryString("update_buffer_lastseen"));
823
824   query.bindValue(":userid", user.toInt());
825   query.bindValue(":bufferid", bufferId.toInt());
826   query.bindValue(":lastseenmsgid", msgId.toInt());
827   safeExec(query);
828   watchQuery(query);
829 }
830
831 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user) {
832   QHash<BufferId, MsgId> lastSeenHash;
833   QSqlQuery query(logDb());
834   query.prepare(queryString("select_buffer_lastseen_messages"));
835   query.bindValue(":userid", user.toInt());
836   safeExec(query);
837   if(!watchQuery(query))
838     return lastSeenHash;
839
840   while(query.next()) {
841     lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
842   }
843   return lastSeenHash;
844 }
845
846 MsgId SqliteStorage::logMessage(Message msg) {
847   QSqlQuery logMessageQuery(logDb());
848   logMessageQuery.prepare(queryString("insert_message"));
849
850   logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
851   logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
852   logMessageQuery.bindValue(":type", msg.type());
853   logMessageQuery.bindValue(":flags", (int)msg.flags());
854   logMessageQuery.bindValue(":sender", msg.sender());
855   logMessageQuery.bindValue(":message", msg.contents());
856   safeExec(logMessageQuery);
857
858   if(logMessageQuery.lastError().isValid()) {
859     // constraint violation - must be NOT NULL constraint - probably the sender is missing...
860     if(logMessageQuery.lastError().number() == 19) {
861       QSqlQuery addSenderQuery(logDb());
862       addSenderQuery.prepare(queryString("insert_sender"));
863       addSenderQuery.bindValue(":sender", msg.sender());
864       safeExec(addSenderQuery);
865       safeExec(logMessageQuery);
866       if(!watchQuery(logMessageQuery))
867         return 0;
868     } else {
869       watchQuery(logMessageQuery);
870     }
871   }
872
873   MsgId msgId = logMessageQuery.lastInsertId().toInt();
874   Q_ASSERT(msgId.isValid());
875   return msgId;
876 }
877
878 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
879   QList<Message> messagelist;
880
881   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
882   if(!bufferInfo.isValid())
883     return messagelist;
884
885   QSqlQuery query(logDb());
886   if(last == -1) {
887     query.prepare(queryString("select_messagesNew"));
888   } else {
889     query.prepare(queryString("select_messages"));
890     query.bindValue(":lastmsg", last.toInt());
891   }
892
893   query.bindValue(":bufferid", bufferId.toInt());
894   query.bindValue(":firstmsg", first.toInt());
895   query.bindValue(":limit", limit);
896   safeExec(query);
897
898   watchQuery(query);
899
900   while(query.next()) {
901     Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
902                 bufferInfo,
903                 (Message::Type)query.value(2).toUInt(),
904                 query.value(5).toString(),
905                 query.value(4).toString(),
906                 (Message::Flags)query.value(3).toUInt());
907     msg.setMsgId(query.value(0).toInt());
908     messagelist << msg;
909   }
910   return messagelist;
911 }
912
913 QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) {
914   QList<Message> messagelist;
915
916   QHash<BufferId, BufferInfo> bufferInfoHash;
917   foreach(BufferInfo bufferInfo, requestBuffers(user)) {
918     bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
919   }
920
921   QSqlQuery query(logDb());
922   if(last == -1) {
923     query.prepare(queryString("select_messagesAllNew"));
924   } else {
925     query.prepare(queryString("select_messagesAll"));
926     query.bindValue(":lastmsg", last.toInt());
927   }
928   query.bindValue(":userid", user.toInt());
929   query.bindValue(":firstmsg", first.toInt());
930   query.bindValue(":limit", limit);
931   safeExec(query);
932
933   watchQuery(query);
934
935   while(query.next()) {
936     Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
937                 bufferInfoHash[query.value(1).toInt()],
938                 (Message::Type)query.value(3).toUInt(),
939                 query.value(6).toString(),
940                 query.value(5).toString(),
941                 (Message::Flags)query.value(4).toUInt());
942     msg.setMsgId(query.value(0).toInt());
943     messagelist << msg;
944   }
945
946   return messagelist;
947 }
948
949 QString SqliteStorage::backlogFile() {
950   return quasselDir().absolutePath() + "/quassel-storage.sqlite";
951 }
952
953 bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount) {
954   query.exec();
955
956   if(!query.lastError().isValid())
957     return true;
958
959   switch(query.lastError().number()) {
960   case 5: // SQLITE_BUSY         5   /* The database file is locked */
961   case 6: // SQLITE_LOCKED       6   /* A table in the database is locked */
962     if(retryCount < _maxRetryCount)
963       return safeExec(query, retryCount + 1);
964   default:
965     return false;
966   }
967 }