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>
27 SqliteStorage::SqliteStorage(QObject *parent)
28 : AbstractSqlStorage(parent)
32 SqliteStorage::~SqliteStorage() {
35 bool SqliteStorage::isAvailable() {
36 if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
40 QString SqliteStorage::displayName() {
41 return QString("SQLite");
44 QString SqliteStorage::engineName() {
45 return SqliteStorage::displayName();
48 int SqliteStorage::installedSchemaVersion() {
49 QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
51 return query.value(0).toInt();
53 // maybe it's really old... (schema version 0)
54 query = logDb().exec("SELECT MAX(version) FROM coreinfo");
56 return query.value(0).toInt();
58 return AbstractSqlStorage::installedSchemaVersion();
61 UserId SqliteStorage::addUser(const QString &user, const QString &password) {
62 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
63 cryptopass = cryptopass.toHex();
65 QSqlQuery query(logDb());
66 query.prepare(queryString("insert_quasseluser"));
67 query.bindValue(":username", user);
68 query.bindValue(":password", cryptopass);
70 if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
74 query.prepare(queryString("select_userid"));
75 query.bindValue(":username", user);
78 UserId uid = query.value(0).toInt();
79 emit userAdded(uid, user);
83 void SqliteStorage::updateUser(UserId user, const QString &password) {
84 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
85 cryptopass = cryptopass.toHex();
87 QSqlQuery query(logDb());
88 query.prepare(queryString("update_userpassword"));
89 query.bindValue(":userid", user.toInt());
90 query.bindValue(":password", cryptopass);
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);
100 emit userRenamed(user, newName);
103 UserId SqliteStorage::validateUser(const QString &user, const QString &password) {
104 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
105 cryptopass = cryptopass.toHex();
107 QSqlQuery query(logDb());
108 query.prepare(queryString("select_authuser"));
109 query.bindValue(":username", user);
110 query.bindValue(":password", cryptopass);
114 return query.value(0).toInt();
120 void SqliteStorage::delUser(UserId user) {
121 QSqlQuery query(logDb());
122 query.prepare(queryString("delete_backlog_by_uid"));
123 query.bindValue(":userid", user.toInt());
126 query.prepare(queryString("delete_buffers_by_uid"));
127 query.bindValue(":userid", user.toInt());
130 query.prepare(queryString("delete_networks_by_uid"));
131 query.bindValue(":userid", user.toInt());
134 query.prepare(queryString("delete_quasseluser"));
135 query.bindValue(":userid", user.toInt());
137 // I hate the lack of foreign keys and on delete cascade... :(
138 emit userRemoved(user);
141 void SqliteStorage::createBuffer(UserId user, const QString &network, const QString &buffer) {
142 QSqlQuery *createBufferQuery = cachedQuery("insert_buffer");
143 createBufferQuery->bindValue(":userid", user.toInt());
144 createBufferQuery->bindValue(":userid2", user.toInt()); // Qt can't handle same placeholder twice (maybe sqlites fault)
145 createBufferQuery->bindValue(":networkname", network);
146 createBufferQuery->bindValue(":buffername", buffer);
147 createBufferQuery->exec();
149 watchQuery(createBufferQuery);
150 if(createBufferQuery->lastError().isValid()) {
151 if(createBufferQuery->lastError().number() == 19) { // Null Constraint violation
152 QSqlQuery *createNetworkQuery = cachedQuery("insert_network");
153 createNetworkQuery->bindValue(":userid", user.toInt());
154 createNetworkQuery->bindValue(":networkname", network);
155 createNetworkQuery->exec();
156 Q_ASSERT(watchQuery(createNetworkQuery));
157 createBufferQuery->exec();
158 Q_ASSERT(watchQuery(createBufferQuery));
161 qDebug() << "failed to create Buffer: ErrNo:" << createBufferQuery->lastError().number() << "ErrMsg:" << createBufferQuery->lastError().text();
167 NetworkId SqliteStorage::getNetworkId(UserId user, const QString &network) {
168 QSqlQuery query(logDb());
169 query.prepare("SELECT networkid FROM network "
170 "WHERE userid = :userid AND networkname = :networkname");
171 query.bindValue(":userid", user.toInt());
172 query.bindValue(":networkname", network);
176 return query.value(0).toInt();
178 createBuffer(user, network, "");
181 return query.value(0).toInt();
183 qWarning() << "NETWORK NOT FOUND:" << network << "for User:" << user;
189 BufferInfo SqliteStorage::getBufferInfo(UserId user, const QString &network, const QString &buffer) {
191 // TODO: get rid of this hackaround
192 NetworkId networkId = getNetworkId(user, network);
194 QSqlQuery *getBufferInfoQuery = cachedQuery("select_bufferByName");
195 getBufferInfoQuery->bindValue(":networkid", networkId.toInt());
196 getBufferInfoQuery->bindValue(":userid", user.toInt());
197 getBufferInfoQuery->bindValue(":buffername", buffer);
198 getBufferInfoQuery->exec();
200 if(!getBufferInfoQuery->first()) {
201 createBuffer(user, network, buffer);
202 getBufferInfoQuery->exec();
203 if(getBufferInfoQuery->first()) {
204 bufferid = BufferInfo(getBufferInfoQuery->value(0).toInt(), networkId, 0, network, buffer);
205 emit bufferInfoUpdated(user, bufferid);
208 bufferid = BufferInfo(getBufferInfoQuery->value(0).toInt(), networkId, 0, network, buffer);
211 Q_ASSERT(!getBufferInfoQuery->next());
216 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user, QDateTime since) {
219 time = since.toTime_t();
221 QList<BufferInfo> bufferlist;
222 QSqlQuery query(logDb());
223 query.prepare(queryString("select_buffers"));
224 query.bindValue(":userid", user.toInt());
225 query.bindValue(":time", time);
229 while(query.next()) {
230 bufferlist << BufferInfo(query.value(0).toInt(), query.value(2).toInt(), 0, query.value(3).toString(), query.value(1).toString());
235 MsgId SqliteStorage::logMessage(Message msg) {
236 QSqlQuery *logMessageQuery = cachedQuery("insert_message");
237 logMessageQuery->bindValue(":time", msg.timestamp().toTime_t());
238 logMessageQuery->bindValue(":bufferid", msg.buffer().uid().toInt());
239 logMessageQuery->bindValue(":type", msg.type());
240 logMessageQuery->bindValue(":flags", msg.flags());
241 logMessageQuery->bindValue(":sender", msg.sender());
242 logMessageQuery->bindValue(":message", msg.text());
243 logMessageQuery->exec();
245 if(logMessageQuery->lastError().isValid()) {
246 // constraint violation - must be NOT NULL constraint - probably the sender is missing...
247 if(logMessageQuery->lastError().number() == 19) {
248 QSqlQuery *addSenderQuery = cachedQuery("insert_sender");
249 addSenderQuery->bindValue(":sender", msg.sender());
250 addSenderQuery->exec();
251 watchQuery(addSenderQuery);
252 logMessageQuery->exec();
253 if(!watchQuery(logMessageQuery))
256 watchQuery(logMessageQuery);
260 QSqlQuery *getLastMessageIdQuery = cachedQuery("select_lastMessage");
261 getLastMessageIdQuery->bindValue(":time", msg.timestamp().toTime_t());
262 getLastMessageIdQuery->bindValue(":bufferid", msg.buffer().uid().toInt());
263 getLastMessageIdQuery->bindValue(":type", msg.type());
264 getLastMessageIdQuery->bindValue(":sender", msg.sender());
265 getLastMessageIdQuery->exec();
267 if(getLastMessageIdQuery->first()) {
268 return getLastMessageIdQuery->value(0).toInt();
269 } else { // somethin went wrong... :(
270 qDebug() << getLastMessageIdQuery->lastQuery() << "time/bufferid/type/sender:" << msg.timestamp().toTime_t() << msg.buffer().uid() << msg.type() << msg.sender();
276 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, int lastmsgs, int offset) {
277 QList<Message> messagelist;
278 // we have to determine the real offset first
279 QSqlQuery *requestMsgsOffsetQuery = cachedQuery("select_messagesOffset");
280 requestMsgsOffsetQuery->bindValue(":bufferid", buffer.uid().toInt());
281 requestMsgsOffsetQuery->bindValue(":messageid", offset);
282 requestMsgsOffsetQuery->exec();
283 requestMsgsOffsetQuery->first();
284 offset = requestMsgsOffsetQuery->value(0).toInt();
286 // now let's select the messages
287 QSqlQuery *requestMsgsQuery = cachedQuery("select_messages");
288 requestMsgsQuery->bindValue(":bufferid", buffer.uid().toInt());
289 requestMsgsQuery->bindValue(":bufferid2", buffer.uid().toInt()); // Qt can't handle the same placeholder used twice
290 requestMsgsQuery->bindValue(":limit", lastmsgs);
291 requestMsgsQuery->bindValue(":offset", offset);
292 requestMsgsQuery->exec();
293 while(requestMsgsQuery->next()) {
294 Message msg(QDateTime::fromTime_t(requestMsgsQuery->value(1).toInt()),
296 (Message::Type)requestMsgsQuery->value(2).toUInt(),
297 requestMsgsQuery->value(5).toString(),
298 requestMsgsQuery->value(4).toString(),
299 requestMsgsQuery->value(3).toUInt());
300 msg.setMsgId(requestMsgsQuery->value(0).toInt());
307 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, QDateTime since, int offset) {
308 QList<Message> messagelist;
309 // we have to determine the real offset first
310 QSqlQuery *requestMsgsSinceOffsetQuery = cachedQuery("select_messagesSinceOffset");
311 requestMsgsSinceOffsetQuery->bindValue(":bufferid", buffer.uid().toInt());
312 requestMsgsSinceOffsetQuery->bindValue(":since", since.toTime_t());
313 requestMsgsSinceOffsetQuery->exec();
314 requestMsgsSinceOffsetQuery->first();
315 offset = requestMsgsSinceOffsetQuery->value(0).toInt();
317 // now let's select the messages
318 QSqlQuery *requestMsgsSinceQuery = cachedQuery("select_messagesSince");
319 requestMsgsSinceQuery->bindValue(":bufferid", buffer.uid().toInt());
320 requestMsgsSinceQuery->bindValue(":bufferid2", buffer.uid().toInt());
321 requestMsgsSinceQuery->bindValue(":since", since.toTime_t());
322 requestMsgsSinceQuery->bindValue(":offset", offset);
323 requestMsgsSinceQuery->exec();
325 while(requestMsgsSinceQuery->next()) {
326 Message msg(QDateTime::fromTime_t(requestMsgsSinceQuery->value(1).toInt()),
328 (Message::Type)requestMsgsSinceQuery->value(2).toUInt(),
329 requestMsgsSinceQuery->value(5).toString(),
330 requestMsgsSinceQuery->value(4).toString(),
331 requestMsgsSinceQuery->value(3).toUInt());
332 msg.setMsgId(requestMsgsSinceQuery->value(0).toInt());
340 QList<Message> SqliteStorage::requestMsgRange(BufferInfo buffer, int first, int last) {
341 QList<Message> messagelist;
342 QSqlQuery *requestMsgRangeQuery = cachedQuery("select_messageRange");
343 requestMsgRangeQuery->bindValue(":bufferid", buffer.uid().toInt());
344 requestMsgRangeQuery->bindValue(":bufferid2", buffer.uid().toInt());
345 requestMsgRangeQuery->bindValue(":firstmsg", first);
346 requestMsgRangeQuery->bindValue(":lastmsg", last);
348 while(requestMsgRangeQuery->next()) {
349 Message msg(QDateTime::fromTime_t(requestMsgRangeQuery->value(1).toInt()),
351 (Message::Type)requestMsgRangeQuery->value(2).toUInt(),
352 requestMsgRangeQuery->value(5).toString(),
353 requestMsgRangeQuery->value(4).toString(),
354 requestMsgRangeQuery->value(3).toUInt());
355 msg.setMsgId(requestMsgRangeQuery->value(0).toInt());
362 QString SqliteStorage::backlogFile() {
363 // kinda ugly, but I currently see no other way to do that
365 QString quasselDir = QDir::homePath() + qgetenv("APPDATA") + "\\quassel\\";
367 QString quasselDir = QDir::homePath() + "/.quassel/";
370 QDir qDir(quasselDir);
371 if(!qDir.exists(quasselDir))
372 qDir.mkpath(quasselDir);
374 return quasselDir + "quassel-storage.sqlite";