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() {
37 if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
41 QString SqliteStorage::displayName() {
42 return QString("SQLite");
45 QString SqliteStorage::engineName() {
46 return SqliteStorage::displayName();
49 int SqliteStorage::installedSchemaVersion() {
50 QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
52 return query.value(0).toInt();
54 // maybe it's really old... (schema version 0)
55 query = logDb().exec("SELECT MAX(version) FROM coreinfo");
57 return query.value(0).toInt();
59 return AbstractSqlStorage::installedSchemaVersion();
62 UserId SqliteStorage::addUser(const QString &user, const QString &password) {
63 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
64 cryptopass = cryptopass.toHex();
66 QSqlQuery query(logDb());
67 query.prepare(queryString("insert_quasseluser"));
68 query.bindValue(":username", user);
69 query.bindValue(":password", cryptopass);
71 if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
75 query.prepare(queryString("select_userid"));
76 query.bindValue(":username", user);
79 UserId uid = query.value(0).toInt();
80 emit userAdded(uid, user);
84 void SqliteStorage::updateUser(UserId user, const QString &password) {
85 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
86 cryptopass = cryptopass.toHex();
88 QSqlQuery query(logDb());
89 query.prepare(queryString("update_userpassword"));
90 query.bindValue(":userid", user.toInt());
91 query.bindValue(":password", cryptopass);
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);
101 emit userRenamed(user, newName);
104 UserId SqliteStorage::validateUser(const QString &user, const QString &password) {
105 QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
106 cryptopass = cryptopass.toHex();
108 QSqlQuery query(logDb());
109 query.prepare(queryString("select_authuser"));
110 query.bindValue(":username", user);
111 query.bindValue(":password", cryptopass);
115 return query.value(0).toInt();
121 void SqliteStorage::delUser(UserId user) {
122 QSqlQuery query(logDb());
123 query.prepare(queryString("delete_backlog_by_uid"));
124 query.bindValue(":userid", user.toInt());
127 query.prepare(queryString("delete_buffers_by_uid"));
128 query.bindValue(":userid", user.toInt());
131 query.prepare(queryString("delete_networks_by_uid"));
132 query.bindValue(":userid", user.toInt());
135 query.prepare(queryString("delete_quasseluser"));
136 query.bindValue(":userid", user.toInt());
138 // I hate the lack of foreign keys and on delete cascade... :(
139 emit userRemoved(user);
142 NetworkId SqliteStorage::createNetworkId(UserId user, const NetworkInfo &info) {
144 QSqlQuery query(logDb());
145 query.prepare(queryString("insert_network"));
146 query.bindValue(":userid", user.toInt());
147 query.bindValue(":networkname", info.networkName);
150 networkId = getNetworkId(user, info.networkName);
151 if(!networkId.isValid()) {
157 NetworkId SqliteStorage::getNetworkId(UserId user, const QString &network) {
158 QSqlQuery query(logDb());
159 query.prepare("SELECT networkid FROM network "
160 "WHERE userid = :userid AND networkname = :networkname");
161 query.bindValue(":userid", user.toInt());
162 query.bindValue(":networkname", network);
166 return query.value(0).toInt();
171 void SqliteStorage::createBuffer(UserId user, const NetworkId &networkId, const QString &buffer) {
172 QSqlQuery *query = cachedQuery("insert_buffer");
173 query->bindValue(":userid", user.toInt());
174 query->bindValue(":networkid", networkId.toInt());
175 query->bindValue(":buffername", buffer);
181 BufferInfo SqliteStorage::getBufferInfo(UserId user, const NetworkId &networkId, const QString &buffer) {
182 QSqlQuery *query = cachedQuery("select_bufferByName");
183 query->bindValue(":networkid", networkId.toInt());
184 query->bindValue(":userid", user.toInt());
185 query->bindValue(":buffername", buffer);
188 if(!query->first()) {
189 createBuffer(user, networkId, buffer);
191 if(!query->first()) {
193 qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
198 BufferInfo bufferInfo = BufferInfo(query->value(0).toInt(), networkId, 0, buffer);
200 qWarning() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
201 qWarning() << " Query:" << query->lastQuery();
202 qWarning() << " bound Values:" << query->boundValues();
209 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user, QDateTime since) {
212 time = since.toTime_t();
214 QList<BufferInfo> bufferlist;
215 QSqlQuery query(logDb());
216 query.prepare(queryString("select_buffers"));
217 query.bindValue(":userid", user.toInt());
218 query.bindValue(":time", time);
222 while(query.next()) {
223 bufferlist << BufferInfo(query.value(0).toInt(), query.value(2).toInt(), 0, query.value(1).toString());
228 MsgId SqliteStorage::logMessage(Message msg) {
229 QSqlQuery *logMessageQuery = cachedQuery("insert_message");
230 logMessageQuery->bindValue(":time", msg.timestamp().toTime_t());
231 logMessageQuery->bindValue(":bufferid", msg.buffer().uid().toInt());
232 logMessageQuery->bindValue(":type", msg.type());
233 logMessageQuery->bindValue(":flags", msg.flags());
234 logMessageQuery->bindValue(":sender", msg.sender());
235 logMessageQuery->bindValue(":message", msg.text());
236 logMessageQuery->exec();
238 if(logMessageQuery->lastError().isValid()) {
239 // constraint violation - must be NOT NULL constraint - probably the sender is missing...
240 if(logMessageQuery->lastError().number() == 19) {
241 QSqlQuery *addSenderQuery = cachedQuery("insert_sender");
242 addSenderQuery->bindValue(":sender", msg.sender());
243 addSenderQuery->exec();
244 watchQuery(addSenderQuery);
245 logMessageQuery->exec();
246 if(!watchQuery(logMessageQuery))
249 watchQuery(logMessageQuery);
253 QSqlQuery *getLastMessageIdQuery = cachedQuery("select_lastMessage");
254 getLastMessageIdQuery->bindValue(":time", msg.timestamp().toTime_t());
255 getLastMessageIdQuery->bindValue(":bufferid", msg.buffer().uid().toInt());
256 getLastMessageIdQuery->bindValue(":type", msg.type());
257 getLastMessageIdQuery->bindValue(":sender", msg.sender());
258 getLastMessageIdQuery->exec();
260 if(getLastMessageIdQuery->first()) {
261 return getLastMessageIdQuery->value(0).toInt();
262 } else { // somethin went wrong... :(
263 qDebug() << getLastMessageIdQuery->lastQuery() << "time/bufferid/type/sender:" << msg.timestamp().toTime_t() << msg.buffer().uid() << msg.type() << msg.sender();
269 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, int lastmsgs, int offset) {
270 QList<Message> messagelist;
271 // we have to determine the real offset first
272 QSqlQuery *requestMsgsOffsetQuery = cachedQuery("select_messagesOffset");
273 requestMsgsOffsetQuery->bindValue(":bufferid", buffer.uid().toInt());
274 requestMsgsOffsetQuery->bindValue(":messageid", offset);
275 requestMsgsOffsetQuery->exec();
276 requestMsgsOffsetQuery->first();
277 offset = requestMsgsOffsetQuery->value(0).toInt();
279 // now let's select the messages
280 QSqlQuery *requestMsgsQuery = cachedQuery("select_messages");
281 requestMsgsQuery->bindValue(":bufferid", buffer.uid().toInt());
282 requestMsgsQuery->bindValue(":bufferid2", buffer.uid().toInt()); // Qt can't handle the same placeholder used twice
283 requestMsgsQuery->bindValue(":limit", lastmsgs);
284 requestMsgsQuery->bindValue(":offset", offset);
285 requestMsgsQuery->exec();
286 while(requestMsgsQuery->next()) {
287 Message msg(QDateTime::fromTime_t(requestMsgsQuery->value(1).toInt()),
289 (Message::Type)requestMsgsQuery->value(2).toUInt(),
290 requestMsgsQuery->value(5).toString(),
291 requestMsgsQuery->value(4).toString(),
292 requestMsgsQuery->value(3).toUInt());
293 msg.setMsgId(requestMsgsQuery->value(0).toInt());
300 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, QDateTime since, int offset) {
301 QList<Message> messagelist;
302 // we have to determine the real offset first
303 QSqlQuery *requestMsgsSinceOffsetQuery = cachedQuery("select_messagesSinceOffset");
304 requestMsgsSinceOffsetQuery->bindValue(":bufferid", buffer.uid().toInt());
305 requestMsgsSinceOffsetQuery->bindValue(":since", since.toTime_t());
306 requestMsgsSinceOffsetQuery->exec();
307 requestMsgsSinceOffsetQuery->first();
308 offset = requestMsgsSinceOffsetQuery->value(0).toInt();
310 // now let's select the messages
311 QSqlQuery *requestMsgsSinceQuery = cachedQuery("select_messagesSince");
312 requestMsgsSinceQuery->bindValue(":bufferid", buffer.uid().toInt());
313 requestMsgsSinceQuery->bindValue(":bufferid2", buffer.uid().toInt());
314 requestMsgsSinceQuery->bindValue(":since", since.toTime_t());
315 requestMsgsSinceQuery->bindValue(":offset", offset);
316 requestMsgsSinceQuery->exec();
318 while(requestMsgsSinceQuery->next()) {
319 Message msg(QDateTime::fromTime_t(requestMsgsSinceQuery->value(1).toInt()),
321 (Message::Type)requestMsgsSinceQuery->value(2).toUInt(),
322 requestMsgsSinceQuery->value(5).toString(),
323 requestMsgsSinceQuery->value(4).toString(),
324 requestMsgsSinceQuery->value(3).toUInt());
325 msg.setMsgId(requestMsgsSinceQuery->value(0).toInt());
333 QList<Message> SqliteStorage::requestMsgRange(BufferInfo buffer, int first, int last) {
334 QList<Message> messagelist;
335 QSqlQuery *requestMsgRangeQuery = cachedQuery("select_messageRange");
336 requestMsgRangeQuery->bindValue(":bufferid", buffer.uid().toInt());
337 requestMsgRangeQuery->bindValue(":bufferid2", buffer.uid().toInt());
338 requestMsgRangeQuery->bindValue(":firstmsg", first);
339 requestMsgRangeQuery->bindValue(":lastmsg", last);
341 while(requestMsgRangeQuery->next()) {
342 Message msg(QDateTime::fromTime_t(requestMsgRangeQuery->value(1).toInt()),
344 (Message::Type)requestMsgRangeQuery->value(2).toUInt(),
345 requestMsgRangeQuery->value(5).toString(),
346 requestMsgRangeQuery->value(4).toString(),
347 requestMsgRangeQuery->value(3).toUInt());
348 msg.setMsgId(requestMsgRangeQuery->value(0).toInt());
355 QString SqliteStorage::backlogFile() {
356 // kinda ugly, but I currently see no other way to do that
358 QString quasselDir = QDir::homePath() + qgetenv("APPDATA") + "\\quassel\\";
360 QString quasselDir = QDir::homePath() + "/.quassel/";
363 QDir qDir(quasselDir);
364 if(!qDir.exists(quasselDir))
365 qDir.mkpath(quasselDir);
367 return quasselDir + "quassel-storage.sqlite";