Fixed and integrated SqliteStorage as new storage backend. This retires the old Backl...
authorManuel Nickschas <sputnick@quassel-irc.org>
Sun, 3 Jun 2007 14:21:01 +0000 (14:21 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 3 Jun 2007 14:21:01 +0000 (14:21 +0000)
SqliteStorage still needs some love though, and it does not yet have transactions.

Now that we have transitioned to Storage, we can tackle multi-user support in Core.

CMakeLists.txt
core/core.cpp
core/core.h
core/server.cpp
core/sqlitestorage.cpp

index 3634639..5a365e5 100644 (file)
@@ -42,7 +42,7 @@ INCLUDE_DIRECTORIES(${SDIRS} plugins)
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
 
 # We need Qt4 support.
-SET(QT_MIN_VERSION "4.3.0")
+SET(QT_MIN_VERSION "4.3.0")  # 4.3 is required for SSL, crypto and some other stuff
 FIND_PACKAGE(Qt4 REQUIRED)
 
 # Set needed libraries
index f9de45f..821ec2a 100644 (file)
@@ -23,6 +23,7 @@
 #include "global.h"
 #include "util.h"
 #include "coreproxy.h"
+#include "sqlitestorage.h"
 
 #include <QtSql>
 #include <QSettings>
 Core::Core() {
   if(core) qFatal("Trying to instantiate more than one Core object!");
 
+  if(!SqliteStorage::isAvailable()) {
+    qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
+  }
+  //SqliteStorage::init();
+  storage = new SqliteStorage();
+  user = storage->validateUser("Default", "password");
+  if(!user) user = storage->addUser("Default", "password");
+  Q_ASSERT(user);
+
   connect(coreProxy, SIGNAL(requestServerStates()), this, SIGNAL(serverStateRequested()));
   connect(coreProxy, SIGNAL(gsRequestConnect(QStringList)), this, SLOT(connectToIrc(QStringList)));
   connect(coreProxy, SIGNAL(gsUserInput(BufferId, QString)), this, SLOT(msgFromGUI(BufferId, QString)));
-  connect(coreProxy, SIGNAL(gsImportBacklog()), &backlog, SLOT(importOldBacklog()));
+  connect(coreProxy, SIGNAL(gsImportBacklog()), storage, SLOT(importOldBacklog()));
   connect(coreProxy, SIGNAL(gsRequestBacklog(BufferId, QVariant, QVariant)), this, SLOT(sendBacklog(BufferId, QVariant, QVariant)));
   connect(this, SIGNAL(displayMsg(Message)), coreProxy, SLOT(csDisplayMsg(Message)));
   connect(this, SIGNAL(displayStatusMsg(QString, QString)), coreProxy, SLOT(csDisplayStatusMsg(QString, QString)));
   connect(this, SIGNAL(backlogData(BufferId, QList<QVariant>, bool)), coreProxy, SLOT(csBacklogData(BufferId, QList<QVariant>, bool)));
-  connect(&backlog, SIGNAL(bufferIdUpdated(BufferId)), coreProxy, SLOT(csUpdateBufferId(BufferId)));
+  connect(storage, SIGNAL(bufferIdUpdated(BufferId)), coreProxy, SLOT(csUpdateBufferId(BufferId)));
   connect(this, SIGNAL(bufferIdUpdated(BufferId)), coreProxy, SLOT(csUpdateBufferId(BufferId)));
   // Read global settings from config file
   QSettings s;
@@ -47,7 +57,7 @@ Core::Core() {
   foreach(key, s.childKeys()) {
     global->updateData(key, s.value(key));
   }
-  backlog.init("Default"); // FIXME
+
   global->updateData("CoreReady", true);
   // Now that we are in sync, we can connect signals to automatically store further updates.
   // I don't think we care if global data changed locally or if it was updated by a client. 
@@ -60,6 +70,7 @@ Core::~Core() {
   //foreach(Server *s, servers) {
   //  delete s;
   //}
+  delete storage;
 }
 
 void Core::globalDataUpdated(QString key) {
@@ -119,22 +130,15 @@ void Core::recvMessageFromServer(Message::Type type, QString target, QString tex
   Q_ASSERT(s);
   BufferId buf;
   if((flags & Message::PrivMsg) && !(flags & Message::Self)) {
-    buf = backlog.getBufferId(s->getNetwork(), nickFromMask(sender));
+    buf = storage->getBufferId(user, s->getNetwork(), nickFromMask(sender));
   } else {
-    buf = backlog.getBufferId(s->getNetwork(), target);
+    buf = storage->getBufferId(user, s->getNetwork(), target);
   }
   Message msg(buf, type, text, sender, flags);
-  msg.msgId = backlog.logMessage(msg);
+  msg.msgId = storage->logMessage(msg); //qDebug() << msg.msgId;
+  Q_ASSERT(msg.msgId);
   emit displayMsg(msg);
 }
-/*
-void Core::recvMessageFromServer(Message msg) {
-  Server *s = qobject_cast<Server*>(sender());
-  Q_ASSERT(s);
-  logMessage(s->getNetwork(), msg);
-  emit displayMsg(s->getNetwork(), msg);
-}
-*/
 
 void Core::recvStatusMsgFromServer(QString msg) {
   Server *s = qobject_cast<Server*>(sender());
@@ -143,7 +147,7 @@ void Core::recvStatusMsgFromServer(QString msg) {
 }
 
 QList<BufferId> Core::getBuffers() {
-  return backlog.requestBuffers();
+  return storage->requestBuffers(user);
 }
 
 void Core::sendBacklog(BufferId id, QVariant v1, QVariant v2) {
@@ -153,7 +157,7 @@ void Core::sendBacklog(BufferId id, QVariant v1, QVariant v2) {
 
 
   } else {
-    msglist = backlog.requestMsgs(id, v1.toInt(), v2.toInt());
+    msglist = storage->requestMsgs(id, v1.toInt(), v2.toInt());
   }
 
   // Send messages out in smaller packages - we don't want to make the signal data too large!
index 459c684..a90a8f6 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "server.h"
 #include "backlog.h"
+#include "storage.h"
 #include "global.h"
 
 class Core : public QObject {
@@ -37,7 +38,6 @@ class Core : public QObject {
 
     Core();
     ~Core();
-    QHash<QString, QList<Message> > getBackLog() { return QHash<QString, QList<Message> >()/*backLog*/; }; // FIXME
     QList<BufferId> getBuffers();
 
   public slots:
@@ -59,32 +59,15 @@ class Core : public QObject {
     void bufferIdUpdated(BufferId);
 
   private slots:
-    //void serverStatesRequested();
     void globalDataUpdated(QString);
     void recvStatusMsgFromServer(QString msg);
-    //void recvMessageFromServer(Message msg);
     void recvMessageFromServer(Message::Type, QString target, QString text, QString sender = "", quint8 flags = Message::None);
     void serverDisconnected(QString net);
 
   private:
-    Backlog backlog;
-    QDir backLogDir;
-    bool backLogEnabled;
+    Storage *storage;
     QHash<QString, Server *> servers;
-    //QHash<QString, QList<Message> > backLog;
-    //QHash<QString, int> netIdx;
-    //QHash<QString, QFile *> logFiles;
-    //QHash<QString, QDataStream *> logStreams;
-    //QHash<QString, QDate> logFileDates;
-    //QHash<QString, QDir> logFileDirs;
-
-    //QSqlDatabase logDb;
-
-    //uint getNetIdx(QString net);
-    //void initBackLog();
-    //void initBackLogOld();
-    //void logMessage(QString, Message);
-    //void logMessageOld(QString, Message);
+    UserId user;
 
 };
 
index f527226..f7d7916 100644 (file)
@@ -870,7 +870,7 @@ void Server::handleCtcpAction(CtcpType ctcptype, QString prefix, QString target,
 void Server::handleCtcpPing(CtcpType ctcptype, QString prefix, QString target, QString param) {
   if(ctcptype == CtcpQuery) {
     ctcpReply(nickFromMask(prefix), "PING", param);
-    emit displayMsg(Message::Plain, "", tr("Received CTCP PING request by %1").arg(prefix));
+    emit displayMsg(Message::Server, "", tr("Received CTCP PING request by %1").arg(prefix));
   } else {
     // display ping answer
   }
@@ -880,7 +880,7 @@ void Server::handleCtcpVersion(CtcpType ctcptype, QString prefix, QString target
   if(ctcptype == CtcpQuery) {
     // FIXME use real Info about quassel :)
     ctcpReply(nickFromMask(prefix), "VERSION", QString("Quassel:pre Release:*nix"));
-    emit displayMsg(Message::Plain, "", tr("Received CTCP VERSION request by %1").arg(prefix));
+    emit displayMsg(Message::Server, "", tr("Received CTCP VERSION request by %1").arg(prefix));
   } else {
     // TODO display Version answer
   }
index ba07cbe..9b6f88d 100644 (file)
@@ -23,7 +23,7 @@
 
 SqliteStorage::SqliteStorage() {
   // TODO I don't think that this path is failsafe for windows users :) 
-  QString backlogFile = Global::quasselDir + "/quassel-backlog-newstyle.sqlite";
+  QString backlogFile = Global::quasselDir + "/quassel-storage.sqlite";
   logDb = QSqlDatabase::addDatabase("QSQLITE");
   logDb.setDatabaseName(backlogFile);
   bool ok = logDb.open();
@@ -34,82 +34,86 @@ SqliteStorage::SqliteStorage() {
     Q_ASSERT(ok);
     return;
   }
-  
+
+  /*
+  NO, we should not start a transaction to check if transactions are supported :-)
   if(!logDb.transaction()) {
     qWarning(tr("Database driver does not support transactions. This might lead to a corrupt database!").toAscii());
   }
-  
+  */
+
   // check if the db schema is up to date
   QSqlQuery query = logDb.exec("SELECT MAX(version) FROM coreinfo");
   if(query.first()) {
-    // TOTO VersionCheck
+    // TODO VersionCheck
     //checkVersion(query.value(0));
     qDebug() << "Sqlite is ready. Quassel Schema Version:" << query.value(0).toUInt();
   } else {
     initDb();
   }
-  
+
   // we will need those pretty often... so let's speed things up:
-  createBufferQuery = new QSqlQuery();
+  createBufferQuery = new QSqlQuery(logDb);
   createBufferQuery->prepare("INSERT INTO buffer (userid, networkid, buffername) VALUES (:userid, (SELECT networkid FROM network WHERE networkname = :networkname), :buffername)");
-  
-  createNetworkQuery = new QSqlQuery();
+
+  createNetworkQuery = new QSqlQuery(logDb);
   createNetworkQuery->prepare("INSERT INTO network (userid, networkname) VALUES (:userid, :networkname)");
-  
-  getBufferIdQuery = new QSqlQuery();
+
+  getBufferIdQuery = new QSqlQuery(logDb);
   getBufferIdQuery->prepare("SELECT bufferid FROM buffer "
                             "JOIN network ON buffer.networkid = network.networkid "
-                            "WHERE network.networkname = :networkname AND buffer.userid = :userid AND buffer.buffername = :buffername");
+                            "WHERE network.networkname = :networkname AND buffer.userid = :userid AND buffer.buffername = :buffername "
+                            "LIMIT 1");
 
-  
-  logMessageQuery = new QSqlQuery();
+
+  logMessageQuery = new QSqlQuery(logDb);
   logMessageQuery->prepare("INSERT INTO backlog (time, bufferid, type, flags, senderid, message) "
                            "VALUES (:time, :bufferid, :type, :flags, (SELECT senderid FROM sender WHERE sender = :sender), :message)");
-  
-  addSenderQuery = new QSqlQuery();
+
+  addSenderQuery = new QSqlQuery(logDb);
   addSenderQuery->prepare("INSERT INTO sender (sender) VALUES (:sender)");
 
-  getLastMessageIdQuery = new QSqlQuery();
+  getLastMessageIdQuery = new QSqlQuery(logDb);
   getLastMessageIdQuery->prepare("SELECT messageid FROM backlog "
                                  "WHERE time = :time AND bufferid = :bufferid AND type = :type AND senderid = (SELECT senderid FROM sender WHERE sender = :sender)");
 
-  requestMsgsOffsetQuery = new QSqlQuery();
+  requestMsgsOffsetQuery = new QSqlQuery(logDb);
   requestMsgsOffsetQuery->prepare("SELECT count(*) FROM backlog WHERE bufferid = :bufferid AND messageid < :messageid");
-  
-  requestMsgsQuery = new QSqlQuery();
+
+  requestMsgsQuery = new QSqlQuery(logDb);
   requestMsgsQuery->prepare("SELECT messageid, time,  type, flags, sender, message, displayname "
                             "FROM backlog "
                             "JOIN buffer ON backlog.bufferid = buffer.bufferid "
                             "JOIN sender ON backlog.senderid = sender.senderid "
                             "LEFT JOIN buffergroup ON buffer.groupid = buffergroup.groupid "
-                            "WHERE buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid) "
+                            "WHERE buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid2) "
                             "ORDER BY messageid DESC "
                             "LIMIT :limit OFFSET :offset");
-  
-  requestMsgsSinceOffsetQuery = new QSqlQuery();
+
+  requestMsgsSinceOffsetQuery = new QSqlQuery(logDb);
   requestMsgsSinceOffsetQuery->prepare("SELECT count(*) FROM backlog WHERE bufferid = :bufferid AND time >= :since");
-  
-  requestMsgsSinceQuery = new QSqlQuery();
+
+  requestMsgsSinceQuery = new QSqlQuery(logDb);
   requestMsgsSinceQuery->prepare("SELECT messageid, time,  type, flags, sender, message, displayname "
                                  "FROM backlog "
                                  "JOIN buffer ON backlog.bufferid = buffer.bufferid "
                                  "JOIN sender ON backlog.senderid = sender.senderid "
                                  "LEFT JOIN buffergroup ON buffer.groupid = buffergroup.groupid "
-                                 "WHERE (buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid)) AND "
+                                 "WHERE (buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid2)) AND "
                                  "backlog.time >= :since "
                                  "ORDER BY messageid DESC "
                                  "LIMIT -1 OFFSET :offset");
-  
-  requestMsgRangeQuery = new QSqlQuery();
+
+  requestMsgRangeQuery = new QSqlQuery(logDb);
   requestMsgRangeQuery->prepare("SELECT messageid, time,  type, flags, sender, message, displayname "
                                 "FROM backlog "
                                 "JOIN buffer ON backlog.bufferid = buffer.bufferid "
                                 "JOIN sender ON backlog.senderid = sender.senderid "
                                 "LEFT JOIN buffergroup ON buffer.groupid = buffergroup.groupid "
-                                "WHERE (buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid)) AND "
+                                "WHERE (buffer.bufferid = :bufferid OR buffer.groupid = (SELECT groupid FROM buffer WHERE bufferid = :bufferid2)) AND "
                                 "backlog.messageid >= :firstmsg AND backlog.messageid <= :lastmsg "
                                 "ORDER BY messageid DESC ");
-  
+
 }
 
 SqliteStorage::~SqliteStorage() {
@@ -125,10 +129,12 @@ SqliteStorage::~SqliteStorage() {
   delete createBufferQuery;
   delete getBufferIdQuery;
   logDb.close();
+  //qDebug() << logDb.lastError().text();
 }
 
 
 void SqliteStorage::initDb() {
+  //logDb.transaction();
   logDb.exec("CREATE TABLE quasseluser ("
              "userid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
              "username TEXT UNIQUE NOT NULL,"
@@ -187,29 +193,31 @@ void SqliteStorage::initDb() {
 }
 
 bool SqliteStorage::isAvailable() {
-  // oh yes we're available... at least I do hope so :)
+  if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
   return true;
 }
 
 QString SqliteStorage::displayName() {
-  // I think the class name is a got start here
+  // I think the class name is a good start here
   return QString("SqliteStorage");
 }
 
 UserId SqliteStorage::addUser(QString user, QString password) {
   QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
   cryptopass = cryptopass.toHex();
-  
+
+  //logDb.transaction();
   QSqlQuery query(logDb);
   query.prepare("INSERT INTO quasseluser (username, password) VALUES (:username, :password)");
   query.bindValue(":username", user);
   query.bindValue(":password", cryptopass);
   query.exec();
   if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
+    logDb.rollback();
     return 0;
   } 
   logDb.commit();
-  
+
   query.prepare("SELECT userid FROM quasseluser WHERE username = :username");
   query.bindValue(":username", user);
   query.exec();
@@ -220,7 +228,8 @@ UserId SqliteStorage::addUser(QString user, QString password) {
 void SqliteStorage::updateUser(UserId user, QString password) {
   QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
   cryptopass = cryptopass.toHex();
-  
+
+  //logDb.transaction();
   QSqlQuery query(logDb);
   query.prepare("UPDATE quasseluser SET password = :password WHERE userid = :userid");
   query.bindValue(":userid", user);
@@ -232,7 +241,7 @@ void SqliteStorage::updateUser(UserId user, QString password) {
 UserId SqliteStorage::validateUser(QString user, QString password) {
   QByteArray cryptopass = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1);
   cryptopass = cryptopass.toHex();
-  
+
   QSqlQuery query(logDb);
   query.prepare("SELECT userid FROM quasseluser WHERE username = :username AND password = :password");
   query.bindValue(":username", user);
@@ -244,11 +253,12 @@ UserId SqliteStorage::validateUser(QString user, QString password) {
   } else {
     return 0;
   }
-  
 }
 
 void SqliteStorage::delUser(UserId user) {
+  //logDb.transaction();
   QSqlQuery query(logDb);
+  // FIXME: backlog has no userid, it's in bufferid
   query.prepare("DELETE FROM backlog WHERE userid = :userid");
   query.bindValue(":userid", user);
   query.exec();
@@ -269,60 +279,89 @@ void SqliteStorage::delUser(UserId user) {
 }
 
 void SqliteStorage::createBuffer(UserId user, QString network, QString buffer) {
-  qDebug() << "creating buffer:" << user << network << buffer;
+  //qDebug() << "creating buffer:" << user << network << buffer;
+  //logDb.transaction();
   createBufferQuery->bindValue(":userid", user);
   createBufferQuery->bindValue(":networkname", network);
   createBufferQuery->bindValue(":buffername", buffer);
   createBufferQuery->exec();
-  
+
   if(createBufferQuery->lastError().isValid()) {
     if(createBufferQuery->lastError().number() == 19) { // Null Constraint violation
       createNetworkQuery->bindValue(":userid", user);
       createNetworkQuery->bindValue(":networkname", network);
       createNetworkQuery->exec();
       createBufferQuery->exec();
+      //qDebug() << "net" << createNetworkQuery->lastError().text();
+      //qDebug() << "buf" << createBufferQuery->lastError().text();
       Q_ASSERT(!createNetworkQuery->lastError().isValid());
       Q_ASSERT(!createBufferQuery->lastError().isValid());
     } else {
       // do panic!
       qDebug() << "failed to create Buffer: ErrNo:" << createBufferQuery->lastError().number() << "ErrMsg:" << createBufferQuery->lastError().text();
+      Q_ASSERT(false);
     }
   }
   logDb.commit();
 }
 
 BufferId SqliteStorage::getBufferId(UserId user, QString network, QString buffer) {
+  if(buffer == "") buffer = "$$$";  // FIXME
+
+  QSqlQuery *getBufferIdQuery = new QSqlQuery(logDb);
+  getBufferIdQuery->prepare("SELECT bufferid FROM buffer "
+      "JOIN network ON buffer.networkid = network.networkid "
+      "WHERE network.networkname = :networkname AND buffer.userid = :userid AND buffer.buffername = :buffername "
+      "LIMIT 1");
+
   getBufferIdQuery->bindValue(":networkname", network);
   getBufferIdQuery->bindValue(":userid", user);
   getBufferIdQuery->bindValue(":buffername", buffer);
   getBufferIdQuery->exec();
 
+  bool flg = false;
   if(!getBufferIdQuery->first()) {
     createBuffer(user, network, buffer);
     getBufferIdQuery->exec();
-    Q_ASSERT(getBufferIdQuery->first());
+    flg = getBufferIdQuery->first();
+    Q_ASSERT(flg);
   }
-  
-  return BufferId(getBufferIdQuery->value(0).toUInt(), network, buffer);
+  if(buffer == "$$$") buffer = "";
+  BufferId result = BufferId(getBufferIdQuery->value(0).toUInt(), network, buffer);
+  //getBufferIdQuery->clear(); // this is active for some reason, which wrecks havoc with later transactions
+  getBufferIdQuery->last();
+  //qDebug() << "active" << getBufferIdQuery->isActive();
+  if(flg) emit bufferIdUpdated(result);
+  delete getBufferIdQuery;
+  return result;
 }
 
 QList<BufferId> SqliteStorage::requestBuffers(UserId user, QDateTime since) {
   QList<BufferId> bufferlist;
   QSqlQuery query(logDb);
-  query.prepare("SELECT bufferid, networkname, buffername FROM buffer "
+  // FIXME: fix query (and make it run in sane time)
+  query.prepare("SELECT buffer.bufferid, networkname, buffername FROM buffer "
+                //"JOIN buffer ON buffer.bufferid = backlog.bufferid "
                 "JOIN network ON buffer.networkid = network.networkid "
-                "JOIN backlog ON buffer.bufferid = backlog.bufferid "
-                "WHERE buffer.userid = :userid AND time >= :time");
-  query.bindValue(":userid", user);
-  query.bindValue(":time", since.toTime_t());
+                //"JOIN backlog ON buffer.bufferid = backlog.bufferid "
+                "WHERE buffer.userid = 1");// AND time >= 0");
+  //query.bindValue(":userid", user);
+  //if(since.isValid()) query.bindValue(":time", since.toTime_t());
+  //else query.bindValue(":time",0);
+  //qDebug() << query.boundValues();
   query.exec();
+  //qDebug() << query.lastError().text();
   while(query.next()) {
-    bufferlist << BufferId(query.value(0).toUInt(), query.value(1).toString(), query.value(2).toString());
+    QString buf = query.value(2).toString();
+    if(buf == "$$$") buf = "";
+    bufferlist << BufferId(query.value(0).toUInt(), query.value(1).toString(), buf);
   }
   return bufferlist;
 }
 
 MsgId SqliteStorage::logMessage(Message msg) {
+  if(msg.sender == "") msg.sender = "$$$";  // FIXME handle empty sender strings in a sane way
+  //logDb.transaction();
   logMessageQuery->bindValue(":time", msg.timeStamp.toTime_t());
   logMessageQuery->bindValue(":bufferid", msg.buffer.uid());
   logMessageQuery->bindValue(":type", msg.type);
@@ -330,8 +369,7 @@ MsgId SqliteStorage::logMessage(Message msg) {
   logMessageQuery->bindValue(":sender", msg.sender);
   logMessageQuery->bindValue(":message", msg.text);
   logMessageQuery->exec();
-
-  // constraint violation - must be NOT NULL constraint - probably the sender is missing... 
+  // constraint violation - must be NOT NULL constraint - probably the sender is missing...
   if(logMessageQuery->lastError().isValid()) {
     if(logMessageQuery->lastError().number() == 19) { 
       addSenderQuery->bindValue(":sender", msg.sender);
@@ -343,16 +381,18 @@ MsgId SqliteStorage::logMessage(Message msg) {
   }
 
   logDb.commit();
-  
+
   getLastMessageIdQuery->bindValue(":time", msg.timeStamp.toTime_t());
   getLastMessageIdQuery->bindValue(":bufferid", msg.buffer.uid());
   getLastMessageIdQuery->bindValue(":type", msg.type);
   getLastMessageIdQuery->bindValue(":sender", msg.sender);
   getLastMessageIdQuery->exec();
-  
+
   if(getLastMessageIdQuery->first()) {
     return getLastMessageIdQuery->value(0).toUInt();
   } else { // somethin went wrong... :(
+    qDebug() << getLastMessageIdQuery->lastQuery();
+    Q_ASSERT(false);
     return 0;
   }
 }
@@ -365,13 +405,13 @@ QList<Message> SqliteStorage::requestMsgs(BufferId buffer, int lastmsgs, int off
   requestMsgsOffsetQuery->exec();
   requestMsgsOffsetQuery->first();
   offset = requestMsgsOffsetQuery->value(0).toUInt();
-  
+
   // now let's select the messages
   requestMsgsQuery->bindValue(":bufferid", buffer.uid());
+  requestMsgsQuery->bindValue(":bufferid2", buffer.uid());  // Qt can't handle the same placeholder used twice
   requestMsgsQuery->bindValue(":limit", lastmsgs);
   requestMsgsQuery->bindValue(":offset", offset);
   requestMsgsQuery->exec();
-  
   while(requestMsgsQuery->next()) {
     Message msg(QDateTime::fromTime_t(requestMsgsQuery->value(1).toInt()),
                 buffer,
@@ -380,9 +420,10 @@ QList<Message> SqliteStorage::requestMsgs(BufferId buffer, int lastmsgs, int off
                 requestMsgsQuery->value(4).toString(),
                 requestMsgsQuery->value(3).toUInt());
     msg.msgId = requestMsgsQuery->value(0).toUInt();
+    if(msg.sender == "$$$") msg.sender = "";  // FIXME
     messagelist << msg;
   }
-  
+
   return messagelist;
 }
 
@@ -395,13 +436,14 @@ QList<Message> SqliteStorage::requestMsgs(BufferId buffer, QDateTime since, int
   requestMsgsSinceOffsetQuery->exec();
   requestMsgsSinceOffsetQuery->first();
   offset = requestMsgsSinceOffsetQuery->value(0).toUInt();  
-  
+
   // now let's select the messages
   requestMsgsSinceQuery->bindValue(":bufferid", buffer.uid());
+  requestMsgsSinceQuery->bindValue(":bufferid2", buffer.uid());
   requestMsgsSinceQuery->bindValue(":since", since.toTime_t());
   requestMsgsSinceQuery->bindValue(":offset", offset);
   requestMsgsSinceQuery->exec();
-  
+
   while(requestMsgsSinceQuery->next()) {
     Message msg(QDateTime::fromTime_t(requestMsgsSinceQuery->value(1).toInt()),
                 buffer,
@@ -410,10 +452,11 @@ QList<Message> SqliteStorage::requestMsgs(BufferId buffer, QDateTime since, int
                 requestMsgsSinceQuery->value(4).toString(),
                 requestMsgsSinceQuery->value(3).toUInt());
     msg.msgId = requestMsgsSinceQuery->value(0).toUInt();
+    if(msg.sender == "$$$") msg.sender = "";  // FIXME
     messagelist << msg;
   }
-  
-    
+
+
   return messagelist;
 }
 
@@ -421,9 +464,10 @@ QList<Message> SqliteStorage::requestMsgs(BufferId buffer, QDateTime since, int
 QList<Message> SqliteStorage::requestMsgRange(BufferId buffer, int first, int last) {
   QList<Message> messagelist;
   requestMsgRangeQuery->bindValue(":bufferid", buffer.uid());
+  requestMsgRangeQuery->bindValue(":bufferid2", buffer.uid());
   requestMsgRangeQuery->bindValue(":firstmsg", first);
   requestMsgRangeQuery->bindValue(":lastmsg", last);
-  
+
   while(requestMsgRangeQuery->next()) {
     Message msg(QDateTime::fromTime_t(requestMsgRangeQuery->value(1).toInt()),
                 buffer,
@@ -432,9 +476,10 @@ QList<Message> SqliteStorage::requestMsgRange(BufferId buffer, int first, int la
                 requestMsgRangeQuery->value(4).toString(),
                 requestMsgRangeQuery->value(3).toUInt());
     msg.msgId = requestMsgRangeQuery->value(0).toUInt();
+    if(msg.sender == "$$$") msg.sender = "";  // FIXME
     messagelist << msg;
   }
-  
+
   return messagelist;
 }
 
@@ -448,6 +493,7 @@ void SqliteStorage::importOldBacklog() {
   } else {
     user = query.value(0).toUInt();
   }
+  // FIXME: backlog does not have userid, we have to select bufferids first
   query.prepare("DELETE FROM backlog WHERE userid = :userid");
   query.bindValue(":userid", user);
   query.exec();