1 /***************************************************************************
2 * Copyright (C) 2005-07 by the Quassel IRC Team *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "sqlitestorage.h"
23 #include <QCryptographicHash>
28 SqliteStorage::SqliteStorage(QObject *parent)
29 : AbstractSqlStorage(parent)
33 SqliteStorage::~SqliteStorage() {
36 bool SqliteStorage::isAvailable() const {
37 if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
41 QString SqliteStorage::displayName() const {
42 return QString("SQLite");
45 QString SqliteStorage::description() const {
46 return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized "
47 "databases that do not require access via network. Use SQLite if your Quassel Core should store its data on the same machine "
48 "it is running on, and if you only expect a few users to use your core.");
51 int SqliteStorage::installedSchemaVersion() {
52 QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
54 return query.value(0).toInt();
56 // maybe it's really old... (schema version 0)
57 query = logDb().exec("SELECT MAX(version) FROM coreinfo");
59 return query.value(0).toInt();
61 return AbstractSqlStorage::installedSchemaVersion();
64 UserId SqliteStorage::addUser(const QString &user, const QString &password) {
65 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
66 cryptopass = cryptopass.toHex();
68 QSqlQuery query(logDb());
69 query.prepare(queryString("insert_quasseluser"));
70 query.bindValue(":username", user);
71 query.bindValue(":password", cryptopass);
73 if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
77 query.prepare(queryString("select_userid"));
78 query.bindValue(":username", user);
81 UserId uid = query.value(0).toInt();
82 emit userAdded(uid, user);
86 void SqliteStorage::updateUser(UserId user, const QString &password) {
87 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
88 cryptopass = cryptopass.toHex();
90 QSqlQuery query(logDb());
91 query.prepare(queryString("update_userpassword"));
92 query.bindValue(":userid", user.toInt());
93 query.bindValue(":password", cryptopass);
97 void SqliteStorage::renameUser(UserId user, const QString &newName) {
98 QSqlQuery query(logDb());
99 query.prepare(queryString("update_username"));
100 query.bindValue(":userid", user.toInt());
101 query.bindValue(":username", newName);
103 emit userRenamed(user, newName);
106 UserId SqliteStorage::validateUser(const QString &user, const QString &password) {
107 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
108 cryptopass = cryptopass.toHex();
110 QSqlQuery query(logDb());
111 query.prepare(queryString("select_authuser"));
112 query.bindValue(":username", user);
113 query.bindValue(":password", cryptopass);
117 return query.value(0).toInt();
123 void SqliteStorage::delUser(UserId user) {
124 QSqlQuery query(logDb());
125 query.prepare(queryString("delete_backlog_by_uid"));
126 query.bindValue(":userid", user.toInt());
129 query.prepare(queryString("delete_buffers_by_uid"));
130 query.bindValue(":userid", user.toInt());
133 query.prepare(queryString("delete_networks_by_uid"));
134 query.bindValue(":userid", user.toInt());
137 query.prepare(queryString("delete_quasseluser"));
138 query.bindValue(":userid", user.toInt());
140 // I hate the lack of foreign keys and on delete cascade... :(
141 emit userRemoved(user);
144 NetworkId SqliteStorage::createNetworkId(UserId user, const NetworkInfo &info) {
146 QSqlQuery query(logDb());
147 query.prepare(queryString("insert_network"));
148 query.bindValue(":userid", user.toInt());
149 query.bindValue(":networkname", info.networkName);
152 networkId = getNetworkId(user, info.networkName);
153 if(!networkId.isValid()) {
159 NetworkId SqliteStorage::getNetworkId(UserId user, const QString &network) {
160 QSqlQuery query(logDb());
161 query.prepare("SELECT networkid FROM network "
162 "WHERE userid = :userid AND networkname = :networkname");
163 query.bindValue(":userid", user.toInt());
164 query.bindValue(":networkname", network);
168 return query.value(0).toInt();
173 void SqliteStorage::createBuffer(UserId user, const NetworkId &networkId, const QString &buffer) {
174 QSqlQuery *query = cachedQuery("insert_buffer");
175 query->bindValue(":userid", user.toInt());
176 query->bindValue(":networkid", networkId.toInt());
177 query->bindValue(":buffername", buffer);
178 query->bindValue(":buffercname", buffer.toLower());
184 BufferInfo SqliteStorage::getBufferInfo(UserId user, const NetworkId &networkId, const QString &buffer) {
185 QSqlQuery *query = cachedQuery("select_bufferByName");
186 query->bindValue(":networkid", networkId.toInt());
187 query->bindValue(":userid", user.toInt());
188 query->bindValue(":buffercname", buffer.toLower());
191 if(!query->first()) {
192 createBuffer(user, networkId, buffer);
194 if(!query->first()) {
196 qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
201 BufferInfo bufferInfo = BufferInfo(query->value(0).toInt(), networkId, 0, buffer);
203 qWarning() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
204 qWarning() << " Query:" << query->lastQuery();
205 qWarning() << " bound Values:" << query->boundValues();
212 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user, QDateTime since) {
215 time = since.toTime_t();
217 QList<BufferInfo> bufferlist;
218 QSqlQuery query(logDb());
219 query.prepare(queryString("select_buffers"));
220 query.bindValue(":userid", user.toInt());
221 query.bindValue(":time", time);
225 while(query.next()) {
226 bufferlist << BufferInfo(query.value(0).toInt(), query.value(2).toInt(), 0, query.value(1).toString());
231 MsgId SqliteStorage::logMessage(Message msg) {
232 QSqlQuery *logMessageQuery = cachedQuery("insert_message");
233 logMessageQuery->bindValue(":time", msg.timestamp().toTime_t());
234 logMessageQuery->bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
235 logMessageQuery->bindValue(":type", msg.type());
236 logMessageQuery->bindValue(":flags", msg.flags());
237 logMessageQuery->bindValue(":sender", msg.sender());
238 logMessageQuery->bindValue(":message", msg.text());
239 logMessageQuery->exec();
241 if(logMessageQuery->lastError().isValid()) {
242 // constraint violation - must be NOT NULL constraint - probably the sender is missing...
243 if(logMessageQuery->lastError().number() == 19) {
244 QSqlQuery *addSenderQuery = cachedQuery("insert_sender");
245 addSenderQuery->bindValue(":sender", msg.sender());
246 addSenderQuery->exec();
247 watchQuery(addSenderQuery);
248 logMessageQuery->exec();
249 if(!watchQuery(logMessageQuery))
252 watchQuery(logMessageQuery);
256 QSqlQuery *getLastMessageIdQuery = cachedQuery("select_lastMessage");
257 getLastMessageIdQuery->bindValue(":time", msg.timestamp().toTime_t());
258 getLastMessageIdQuery->bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
259 getLastMessageIdQuery->bindValue(":type", msg.type());
260 getLastMessageIdQuery->bindValue(":sender", msg.sender());
261 getLastMessageIdQuery->exec();
263 if(getLastMessageIdQuery->first()) {
264 return getLastMessageIdQuery->value(0).toInt();
265 } else { // somethin went wrong... :(
266 qDebug() << getLastMessageIdQuery->lastQuery() << "time/bufferid/type/sender:" << msg.timestamp().toTime_t() << msg.bufferInfo().bufferId() << msg.type() << msg.sender();
272 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, int lastmsgs, int offset) {
273 QList<Message> messagelist;
274 // we have to determine the real offset first
275 QSqlQuery *offsetQuery = cachedQuery("select_messagesOffset");
276 offsetQuery->bindValue(":bufferid", buffer.bufferId().toInt());
277 offsetQuery->bindValue(":messageid", offset);
279 offsetQuery->first();
280 offset = offsetQuery->value(0).toInt();
282 // now let's select the messages
283 QSqlQuery *msgQuery = cachedQuery("select_messages");
284 msgQuery->bindValue(":bufferid", buffer.bufferId().toInt());
285 msgQuery->bindValue(":limit", lastmsgs);
286 msgQuery->bindValue(":offset", offset);
289 watchQuery(msgQuery);
291 while(msgQuery->next()) {
292 Message msg(QDateTime::fromTime_t(msgQuery->value(1).toInt()),
294 (Message::Type)msgQuery->value(2).toUInt(),
295 msgQuery->value(5).toString(),
296 msgQuery->value(4).toString(),
297 msgQuery->value(3).toUInt());
298 msg.setMsgId(msgQuery->value(0).toInt());
305 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, QDateTime since, int offset) {
306 QList<Message> messagelist;
307 // we have to determine the real offset first
308 QSqlQuery *offsetQuery = cachedQuery("select_messagesSinceOffset");
309 offsetQuery->bindValue(":bufferid", buffer.bufferId().toInt());
310 offsetQuery->bindValue(":since", since.toTime_t());
312 offsetQuery->first();
313 offset = offsetQuery->value(0).toInt();
315 // now let's select the messages
316 QSqlQuery *msgQuery = cachedQuery("select_messagesSince");
317 msgQuery->bindValue(":bufferid", buffer.bufferId().toInt());
318 msgQuery->bindValue(":since", since.toTime_t());
319 msgQuery->bindValue(":offset", offset);
322 watchQuery(msgQuery);
324 while(msgQuery->next()) {
325 Message msg(QDateTime::fromTime_t(msgQuery->value(1).toInt()),
327 (Message::Type)msgQuery->value(2).toUInt(),
328 msgQuery->value(5).toString(),
329 msgQuery->value(4).toString(),
330 msgQuery->value(3).toUInt());
331 msg.setMsgId(msgQuery->value(0).toInt());
339 QList<Message> SqliteStorage::requestMsgRange(BufferInfo buffer, int first, int last) {
340 QList<Message> messagelist;
341 QSqlQuery *rangeQuery = cachedQuery("select_messageRange");
342 rangeQuery->bindValue(":bufferid", buffer.bufferId().toInt());
343 rangeQuery->bindValue(":firstmsg", first);
344 rangeQuery->bindValue(":lastmsg", last);
347 watchQuery(rangeQuery);
349 while(rangeQuery->next()) {
350 Message msg(QDateTime::fromTime_t(rangeQuery->value(1).toInt()),
352 (Message::Type)rangeQuery->value(2).toUInt(),
353 rangeQuery->value(5).toString(),
354 rangeQuery->value(4).toString(),
355 rangeQuery->value(3).toUInt());
356 msg.setMsgId(rangeQuery->value(0).toInt());
363 QString SqliteStorage::backlogFile() {
364 // kinda ugly, but I currently see no other way to do that
366 QString quasselDir = QDir::homePath() + qgetenv("APPDATA") + "\\quassel\\";
368 QString quasselDir = QDir::homePath() + "/.quassel/";
371 QDir qDir(quasselDir);
372 if(!qDir.exists(quasselDir))
373 qDir.mkpath(quasselDir);
375 return quasselDir + "quassel-storage.sqlite";