52846cdac6b4b761539875d234f072c2be06dc53
[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 SqliteStorage::SqliteStorage() {
26   logMessageQuery = NULL;
27   addSenderQuery = NULL;
28   getLastMessageIdQuery = NULL;
29   requestMsgsQuery = NULL;
30   requestMsgsOffsetQuery = NULL;
31   requestMsgsSinceQuery = NULL;
32   requestMsgsSinceOffsetQuery = NULL;
33   requestMsgRangeQuery = NULL;
34   createNetworkQuery = NULL;
35   createBufferQuery = NULL;
36   getBufferInfoQuery = NULL;
37 }
38
39 SqliteStorage::~SqliteStorage() {
40   if (logMessageQuery) delete logMessageQuery;
41   if (addSenderQuery) delete addSenderQuery;
42   if (getLastMessageIdQuery) delete getLastMessageIdQuery;
43   if (requestMsgsQuery) delete requestMsgsQuery;
44   if (requestMsgsOffsetQuery) delete requestMsgsOffsetQuery;
45   if (requestMsgsSinceQuery) delete requestMsgsSinceQuery;
46   if (requestMsgsSinceOffsetQuery) delete requestMsgsSinceOffsetQuery;
47   if (requestMsgRangeQuery) delete requestMsgRangeQuery;
48   if (createNetworkQuery) delete createNetworkQuery;
49   if (createBufferQuery) delete createBufferQuery;
50   if (getBufferInfoQuery) delete getBufferInfoQuery;
51   
52   logDb.close();
53 }
54
55 bool SqliteStorage::isAvailable() {
56   if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
57   return true;
58 }
59
60 QString SqliteStorage::displayName() {
61   return QString("SQlite");
62 }
63
64 bool SqliteStorage::setup(const QVariantMap &settings) {
65   bool ok;
66   // this extra scope is needed to be able to remove the database connection later
67   {
68     logDb = QSqlDatabase::addDatabase("QSQLITE", "quassel_setup");
69     logDb.setDatabaseName(SqliteStorage::backlogFile(true));
70     ok = logDb.open();
71     
72     if (!ok) {
73       qWarning(tr("Could not open backlog database: %1").arg(logDb.lastError().text()).toAscii());
74     } else {
75       logDb.exec("CREATE TABLE quasseluser ("
76                      "userid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
77                      "username TEXT UNIQUE NOT NULL,"
78                      "password BLOB NOT NULL)");
79           
80       logDb.exec("CREATE TABLE sender ("
81                      "senderid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
82                      "sender TEXT UNIQUE NOT NULL)");
83           
84       logDb.exec("CREATE TABLE network ("
85                      "networkid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
86                      "userid INTEGER NOT NULL,"
87                      "networkname TEXT NOT NULL,"
88                      "UNIQUE (userid, networkname))");
89           
90       logDb.exec("CREATE TABLE buffergroup ("
91                      "groupid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
92                      "userid INTEGER NOT NULL,"
93                      "displayname TEXT)");
94           
95       logDb.exec("CREATE TABLE buffer ("
96                      "bufferid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
97                      "userid INTEGER NOT NULL,"
98                      "groupid INTEGER,"
99                      "networkid INTEGER NOT NULL,"
100                      "buffername TEXT NOT NULL)");
101           
102       logDb.exec("CREATE UNIQUE INDEX buffer_idx "
103                      "ON buffer(userid, networkid, buffername)");
104             
105       logDb.exec("CREATE TABLE backlog ("
106                      "messageid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
107                      "time INTEGER NOT NULL,"
108                      "bufferid INTEGER NOT NULL,"
109                      "type INTEGER NOT NULL,"
110                      "flags INTEGER NOT NULL,"
111                      "senderid INTEGER NOT NULL,"
112                      "message TEXT)");
113           
114       logDb.exec("CREATE TABLE coreinfo ("
115                      "updateid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
116                      "version INTEGER NOT NULL)");
117           
118       logDb.exec("INSERT INTO coreinfo (version) VALUES (0)");
119       
120       // something fucked up -> no logging possible
121       // FIXME logDb.lastError is reset whenever exec is called
122       if(logDb.lastError().isValid()) { 
123         qWarning(tr("Could not create backlog table: %1").arg(logDb.lastError().text()).toAscii());
124         qWarning(tr("Disabling logging...").toAscii());
125         Q_ASSERT(false); // quassel does require logging
126         ok = false;
127       }
128     
129       logDb.close();
130     }
131   } 
132   
133   QSqlDatabase::removeDatabase("quassel_setup");
134   return ok;
135 }
136
137 bool SqliteStorage::init(const QVariantMap &settings) {
138   bool ok;
139   // i need the extra scope to be able to remove the database connection
140   {
141     logDb = QSqlDatabase::database("quassel_connection", false);
142     if (!logDb.isValid()) {
143       logDb = QSqlDatabase::addDatabase("QSQLITE", "quassel_connection");
144     }
145     logDb.setDatabaseName(SqliteStorage::backlogFile());
146     ok = logDb.open();
147     if (!ok) {
148       qWarning(tr("Could not open backlog database: %1").arg(logDb.lastError().text()).toAscii());
149     }
150   }
151
152   if (!ok) {
153     //QSqlDatabase::removeDatabase("quassel_connection");
154     return false;
155   }
156
157   // check if the db schema is up to date
158   QSqlQuery query = logDb.exec("SELECT MAX(version) FROM coreinfo");
159   if(query.first()) {
160     // TODO VersionCheck
161     //checkVersion(query.value(0));
162     qDebug() << "Sqlite is ready. Quassel Schema Version:" << query.value(0).toUInt();
163   } else {
164     qWarning("Sqlite is not ready!");
165     return false;
166   }
167
168   // we will need those pretty often... so let's speed things up:
169   createBufferQuery = new QSqlQuery(logDb);
170   createBufferQuery->prepare("INSERT INTO buffer (userid, networkid, buffername) VALUES (:userid, (SELECT networkid FROM network WHERE networkname = :networkname AND userid = :userid2), :buffername)");
171
172   createNetworkQuery = new QSqlQuery(logDb);
173   createNetworkQuery->prepare("INSERT INTO network (userid, networkname) VALUES (:userid, :networkname)");
174
175   getBufferInfoQuery = new QSqlQuery(logDb);
176   getBufferInfoQuery->prepare("SELECT bufferid FROM buffer "
177                             "JOIN network ON buffer.networkid = network.networkid "
178                             "WHERE network.networkname = :networkname AND network.userid = :userid AND buffer.userid = :userid2 AND lower(buffer.buffername) = lower(:buffername)");
179
180   logMessageQuery = new QSqlQuery(logDb);
181   logMessageQuery->prepare("INSERT INTO backlog (time, bufferid, type, flags, senderid, message) "
182                            "VALUES (:time, :bufferid, :type, :flags, (SELECT senderid FROM sender WHERE sender = :sender), :message)");
183
184   addSenderQuery = new QSqlQuery(logDb);
185   addSenderQuery->prepare("INSERT INTO sender (sender) VALUES (:sender)");
186
187   getLastMessageIdQuery = new QSqlQuery(logDb);
188   getLastMessageIdQuery->prepare("SELECT messageid FROM backlog "
189                                  "WHERE time = :time AND bufferid = :bufferid AND type = :type AND senderid = (SELECT senderid FROM sender WHERE sender = :sender)");
190
191   requestMsgsOffsetQuery = new QSqlQuery(logDb);
192   requestMsgsOffsetQuery->prepare("SELECT count(*) FROM backlog WHERE bufferid = :bufferid AND messageid < :messageid");
193
194   requestMsgsQuery = new QSqlQuery(logDb);
195   requestMsgsQuery->prepare("SELECT messageid, time,  type, flags, sender, message, displayname "
196                             "FROM backlog "
197                             "JOIN buffer ON backlog.bufferid = buffer.bufferid "
198                             "JOIN sender ON backlog.senderid = sender.senderid "
199                             "LEFT JOIN buffergroup ON buffer.groupid = buffergroup.groupid "
200                             "WHERE buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid2) "
201                             "ORDER BY messageid DESC "
202                             "LIMIT :limit OFFSET :offset");
203
204   requestMsgsSinceOffsetQuery = new QSqlQuery(logDb);
205   requestMsgsSinceOffsetQuery->prepare("SELECT count(*) FROM backlog WHERE bufferid = :bufferid AND time >= :since");
206
207   requestMsgsSinceQuery = new QSqlQuery(logDb);
208   requestMsgsSinceQuery->prepare("SELECT messageid, time,  type, flags, sender, message, displayname "
209                                  "FROM backlog "
210                                  "JOIN buffer ON backlog.bufferid = buffer.bufferid "
211                                  "JOIN sender ON backlog.senderid = sender.senderid "
212                                  "LEFT JOIN buffergroup ON buffer.groupid = buffergroup.groupid "
213                                  "WHERE (buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid2)) AND "
214                                  "backlog.time >= :since "
215                                  "ORDER BY messageid DESC "
216                                  "LIMIT -1 OFFSET :offset");
217
218   requestMsgRangeQuery = new QSqlQuery(logDb);
219   requestMsgRangeQuery->prepare("SELECT messageid, time,  type, flags, sender, message, displayname "
220                                 "FROM backlog "
221                                 "JOIN buffer ON backlog.bufferid = buffer.bufferid "
222                                 "JOIN sender ON backlog.senderid = sender.senderid "
223                                 "LEFT JOIN buffergroup ON buffer.groupid = buffergroup.groupid "
224                                 "WHERE (buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid2)) AND "
225                                 "backlog.messageid >= :firstmsg AND backlog.messageid <= :lastmsg "
226                                 "ORDER BY messageid DESC ");
227   
228   return true;
229 }
230
231 UserId SqliteStorage::addUser(const QString &user, const QString &password) {
232   QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
233   cryptopass = cryptopass.toHex();
234
235   QSqlQuery query(logDb);
236   query.prepare("INSERT INTO quasseluser (username, password) VALUES (:username, :password)");
237   query.bindValue(":username", user);
238   query.bindValue(":password", cryptopass);
239   query.exec();
240   if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
241     return 0;
242   }
243
244   query.prepare("SELECT userid FROM quasseluser WHERE username = :username");
245   query.bindValue(":username", user);
246   query.exec();
247   query.first();
248   UserId uid = query.value(0).toUInt();
249   emit userAdded(uid, user);
250   return uid;
251 }
252
253 void SqliteStorage::updateUser(UserId user, const QString &password) {
254   QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
255   cryptopass = cryptopass.toHex();
256
257   QSqlQuery query(logDb);
258   query.prepare("UPDATE quasseluser SET password = :password WHERE userid = :userid");
259   query.bindValue(":userid", user);
260   query.bindValue(":password", cryptopass);
261   query.exec();
262 }
263
264 void SqliteStorage::renameUser(UserId user, const QString &newName) {
265   QSqlQuery query(logDb);
266   query.prepare("UPDATE quasseluser SET username = :username WHERE userid = :userid");
267   query.bindValue(":userid", user);
268   query.bindValue(":username", newName);
269   query.exec();
270   emit userRenamed(user, newName);
271 }
272
273 UserId SqliteStorage::validateUser(const QString &user, const QString &password) {
274   QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
275   cryptopass = cryptopass.toHex();
276
277   QSqlQuery query(logDb);
278   query.prepare("SELECT userid FROM quasseluser WHERE username = :username AND password = :password");
279   query.bindValue(":username", user);
280   query.bindValue(":password", cryptopass);
281   query.exec();
282
283   if(query.first()) {
284     return query.value(0).toUInt();
285   } else {
286     throw AuthError();
287     //return 0;
288   }
289 }
290
291 void SqliteStorage::delUser(UserId user) {
292   QSqlQuery query(logDb);
293   query.prepare("DELETE FROM backlog WHERE bufferid IN (SELECT DISTINCT bufferid FROM buffer WHERE userid = :userid");
294   query.bindValue(":userid", user);
295   query.exec();
296   query.prepare("DELETE FROM buffer WHERE userid = :userid");
297   query.bindValue(":userid", user);
298   query.exec();
299   query.prepare("DELETE FROM buffergroup WHERE userid = :userid");
300   query.bindValue(":userid", user);
301   query.exec();
302   query.prepare("DELETE FROM network WHERE userid = :userid");
303   query.bindValue(":userid", user);
304   query.exec();
305   query.prepare("DELETE FROM quasseluser WHERE userid = :userid");
306   query.bindValue(":userid", user);
307   query.exec();
308   // I hate the lack of foreign keys and on delete cascade... :(
309   emit userRemoved(user);
310 }
311
312 void SqliteStorage::createBuffer(UserId user, const QString &network, const QString &buffer) {
313   createBufferQuery->bindValue(":userid", user);
314   createBufferQuery->bindValue(":userid2", user);  // Qt can't handle same placeholder twice (maybe sqlites fault)
315   createBufferQuery->bindValue(":networkname", network);
316   createBufferQuery->bindValue(":buffername", buffer);
317   createBufferQuery->exec();
318
319   if(createBufferQuery->lastError().isValid()) {
320     if(createBufferQuery->lastError().number() == 19) { // Null Constraint violation 
321       createNetworkQuery->bindValue(":userid", user);
322       createNetworkQuery->bindValue(":networkname", network);
323       createNetworkQuery->exec();
324       createBufferQuery->exec();
325       Q_ASSERT(!createNetworkQuery->lastError().isValid());
326       Q_ASSERT(!createBufferQuery->lastError().isValid());
327     } else {
328       // do panic!
329       qDebug() << "failed to create Buffer: ErrNo:" << createBufferQuery->lastError().number() << "ErrMsg:" << createBufferQuery->lastError().text();
330       Q_ASSERT(false);
331     }
332   }
333 }
334
335 uint SqliteStorage::getNetworkId(UserId user, const QString &network) {
336   QSqlQuery query(logDb);
337   query.prepare("SELECT networkid FROM network "
338                 "WHERE userid = :userid AND networkname = :networkname");
339   query.bindValue(":userid", user);
340   query.bindValue(":networkname", network);
341   query.exec();
342   
343   if(query.first())
344     return query.value(0).toUInt();
345   else {
346     createBuffer(user, network, "");
347     query.exec();
348     if(query.first())
349       return query.value(0).toUInt();
350     else {
351       qWarning() << "NETWORK NOT FOUND:" << network << "for User:" << user;
352       return 0;
353     }
354   }
355 }
356
357 BufferInfo SqliteStorage::getBufferInfo(UserId user, const QString &network, const QString &buffer) {
358   BufferInfo bufferid;
359   // TODO: get rid of this hackaround
360   uint networkId = getNetworkId(user, network);
361   getBufferInfoQuery->bindValue(":networkname", network);
362   getBufferInfoQuery->bindValue(":userid", user);
363   getBufferInfoQuery->bindValue(":userid2", user); // Qt can't handle same placeholder twice... though I guess it's sqlites fault
364   getBufferInfoQuery->bindValue(":buffername", buffer);
365   getBufferInfoQuery->exec();
366
367   if(!getBufferInfoQuery->first()) {
368     createBuffer(user, network, buffer);
369     getBufferInfoQuery->exec();
370     if(getBufferInfoQuery->first()) {
371       bufferid = BufferInfo(getBufferInfoQuery->value(0).toUInt(), networkId, 0, network, buffer);
372       emit bufferInfoUpdated(bufferid);
373     }
374   } else {
375     bufferid = BufferInfo(getBufferInfoQuery->value(0).toUInt(), networkId, 0, network, buffer);
376   }
377
378   Q_ASSERT(!getBufferInfoQuery->next());
379
380   return bufferid;
381 }
382
383 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user, QDateTime since) {
384   QList<BufferInfo> bufferlist;
385   QSqlQuery query(logDb);
386   query.prepare("SELECT DISTINCT buffer.bufferid, networkname, buffername FROM buffer "
387                 "JOIN network ON buffer.networkid = network.networkid "
388                 "JOIN backlog ON buffer.bufferid = backlog.bufferid "
389                 "WHERE buffer.userid = :userid AND time >= :time");
390   query.bindValue(":userid", user);
391   if (since.isValid()) {
392     query.bindValue(":time", since.toTime_t());
393   } else {
394     query.bindValue(":time", 0);
395   }
396   
397   query.exec();
398
399   while(query.next()) {
400     bufferlist << BufferInfo(query.value(0).toUInt(), getNetworkId(user, query.value(1).toString()), 0, query.value(1).toString(), query.value(2).toString());
401   }
402   return bufferlist;
403 }
404
405 MsgId SqliteStorage::logMessage(Message msg) {
406   logMessageQuery->bindValue(":time", msg.timestamp().toTime_t());
407   logMessageQuery->bindValue(":bufferid", msg.buffer().uid());
408   logMessageQuery->bindValue(":type", msg.type());
409   logMessageQuery->bindValue(":flags", msg.flags());
410   logMessageQuery->bindValue(":sender", msg.sender());
411   logMessageQuery->bindValue(":message", msg.text());
412   logMessageQuery->exec();
413   
414   if(logMessageQuery->lastError().isValid()) {
415     // constraint violation - must be NOT NULL constraint - probably the sender is missing...
416     if(logMessageQuery->lastError().number() == 19) { 
417       addSenderQuery->bindValue(":sender", msg.sender());
418       addSenderQuery->exec();
419       watchQuery(addSenderQuery);
420       logMessageQuery->exec();
421       if(!watchQuery(logMessageQuery))
422         return 0;
423     } else {
424       watchQuery(logMessageQuery);
425     }
426   }
427
428   getLastMessageIdQuery->bindValue(":time", msg.timestamp().toTime_t());
429   getLastMessageIdQuery->bindValue(":bufferid", msg.buffer().uid());
430   getLastMessageIdQuery->bindValue(":type", msg.type());
431   getLastMessageIdQuery->bindValue(":sender", msg.sender());
432   getLastMessageIdQuery->exec();
433
434   if(getLastMessageIdQuery->first()) {
435     return getLastMessageIdQuery->value(0).toUInt();
436   } else { // somethin went wrong... :(
437     qDebug() << getLastMessageIdQuery->lastQuery() << "time/bufferid/type/sender:" << msg.timestamp().toTime_t() << msg.buffer().uid() << msg.type() << msg.sender();
438     Q_ASSERT(false);
439     return 0;
440   }
441 }
442
443 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, int lastmsgs, int offset) {
444   QList<Message> messagelist;
445   // we have to determine the real offset first
446   requestMsgsOffsetQuery->bindValue(":bufferid", buffer.uid());
447   requestMsgsOffsetQuery->bindValue(":messageid", offset);
448   requestMsgsOffsetQuery->exec();
449   requestMsgsOffsetQuery->first();
450   offset = requestMsgsOffsetQuery->value(0).toUInt();
451
452   // now let's select the messages
453   requestMsgsQuery->bindValue(":bufferid", buffer.uid());
454   requestMsgsQuery->bindValue(":bufferid2", buffer.uid());  // Qt can't handle the same placeholder used twice
455   requestMsgsQuery->bindValue(":limit", lastmsgs);
456   requestMsgsQuery->bindValue(":offset", offset);
457   requestMsgsQuery->exec();
458   while(requestMsgsQuery->next()) {
459     Message msg(QDateTime::fromTime_t(requestMsgsQuery->value(1).toInt()),
460                 buffer,
461                 (Message::Type)requestMsgsQuery->value(2).toUInt(),
462                 requestMsgsQuery->value(5).toString(),
463                 requestMsgsQuery->value(4).toString(),
464                 requestMsgsQuery->value(3).toUInt());
465     msg.setMsgId(requestMsgsQuery->value(0).toUInt());
466     messagelist << msg;
467   }
468   return messagelist;
469 }
470
471
472 QList<Message> SqliteStorage::requestMsgs(BufferInfo buffer, QDateTime since, int offset) {
473   QList<Message> messagelist;
474   // we have to determine the real offset first
475   requestMsgsSinceOffsetQuery->bindValue(":bufferid", buffer.uid());
476   requestMsgsSinceOffsetQuery->bindValue(":since", since.toTime_t());
477   requestMsgsSinceOffsetQuery->exec();
478   requestMsgsSinceOffsetQuery->first();
479   offset = requestMsgsSinceOffsetQuery->value(0).toUInt();  
480
481   // now let's select the messages
482   requestMsgsSinceQuery->bindValue(":bufferid", buffer.uid());
483   requestMsgsSinceQuery->bindValue(":bufferid2", buffer.uid());
484   requestMsgsSinceQuery->bindValue(":since", since.toTime_t());
485   requestMsgsSinceQuery->bindValue(":offset", offset);
486   requestMsgsSinceQuery->exec();
487
488   while(requestMsgsSinceQuery->next()) {
489     Message msg(QDateTime::fromTime_t(requestMsgsSinceQuery->value(1).toInt()),
490                 buffer,
491                 (Message::Type)requestMsgsSinceQuery->value(2).toUInt(),
492                 requestMsgsSinceQuery->value(5).toString(),
493                 requestMsgsSinceQuery->value(4).toString(),
494                 requestMsgsSinceQuery->value(3).toUInt());
495     msg.setMsgId(requestMsgsSinceQuery->value(0).toUInt());
496     messagelist << msg;
497   }
498
499   return messagelist;
500 }
501
502
503 QList<Message> SqliteStorage::requestMsgRange(BufferInfo buffer, int first, int last) {
504   QList<Message> messagelist;
505   requestMsgRangeQuery->bindValue(":bufferid", buffer.uid());
506   requestMsgRangeQuery->bindValue(":bufferid2", buffer.uid());
507   requestMsgRangeQuery->bindValue(":firstmsg", first);
508   requestMsgRangeQuery->bindValue(":lastmsg", last);
509
510   while(requestMsgRangeQuery->next()) {
511     Message msg(QDateTime::fromTime_t(requestMsgRangeQuery->value(1).toInt()),
512                 buffer,
513                 (Message::Type)requestMsgRangeQuery->value(2).toUInt(),
514                 requestMsgRangeQuery->value(5).toString(),
515                 requestMsgRangeQuery->value(4).toString(),
516                 requestMsgRangeQuery->value(3).toUInt());
517     msg.setMsgId(requestMsgRangeQuery->value(0).toUInt());
518     messagelist << msg;
519   }
520
521   return messagelist;
522 }
523
524 QString SqliteStorage::backlogFile(bool createPath) {
525   // kinda ugly, but I currently see no other way to do that
526 #ifdef Q_OS_WIN32
527   QString quasselDir = QDir::homePath() + qgetenv("APPDATA") + "\\quassel\\";
528 #else
529   QString quasselDir = QDir::homePath() + "/.quassel/";
530 #endif
531   
532   if (createPath) {
533     QDir *qDir = new QDir(quasselDir);
534     if (!qDir->exists(quasselDir)) {
535       qDir->mkpath(quasselDir);
536     }
537     delete qDir;
538   }
539
540   return quasselDir + "quassel-storage.sqlite";
541 }
542
543 bool SqliteStorage::watchQuery(QSqlQuery *query) {
544   if(query->lastError().isValid()) {
545     qWarning() << "unhandled Error in QSqlQuery!";
546     qWarning() << "                  last Query:" << query->lastQuery();
547     qWarning() << "              executed Query:" << query->executedQuery();
548     qWarning() << "                bound Values:" << query->boundValues();
549     qWarning() << "                Error Number:" << query->lastError().number();
550     qWarning() << "               Error Message:" << query->lastError().text();
551     qWarning() << "              Driver Message:" << query->lastError().driverText();
552     qWarning() << "                  DB Message:" << query->lastError().databaseText();
553     
554     return false;
555   }
556   return true;
557 }