INSERT INTO backlog (time, bufferid, type, flags, senderid, message)
-VALUES (:time, :bufferid, :type, :flags, (SELECT senderid FROM sender WHERE sender = :sender), :message)
+VALUES ($1, $2, $3, $4, (SELECT senderid FROM sender WHERE sender = $5), $6)
RETURNING messageid
INSERT INTO sender (sender)
-VALUES (:sender)
\ No newline at end of file
+VALUES ($1)
\ No newline at end of file
_network(parent),
initDone(false)
{
- connect(this, SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)),
- network(), SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)));
+ connect(this, SIGNAL(displayMsg(Message::Type, BufferInfo::Type, const QString &, const QString &, const QString &, Message::Flags)),
+ network(), SLOT(displayMsg(Message::Type, BufferInfo::Type, const QString &, const QString &, const QString &, Message::Flags)));
connect(this, SIGNAL(putCmd(QString, const QList<QByteArray> &, const QByteArray &)),
network(), SLOT(putCmd(QString, const QList<QByteArray> &, const QByteArray &)));
emit putCmd(cmd, list, prefix);
}
-void BasicHandler::displayMsg(Message::Type msgType, QString target, QString text, QString sender, Message::Flags flags) {
+void BasicHandler::displayMsg(Message::Type msgType, QString target, const QString &text, const QString &sender, Message::Flags flags) {
if(!target.isEmpty() && network()->prefixes().contains(target[0]))
target = target.mid(1);
QList<QByteArray> userEncode(const QString &userNick, const QStringList &stringlist);
signals:
- void displayMsg(Message::Type, BufferInfo::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None);
+ void displayMsg(Message::Type, BufferInfo::Type, const QString &target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None);
void putCmd(const QString &cmd, const QList<QByteArray> ¶ms, const QByteArray &prefix = QByteArray());
void putRawLine(const QByteArray &msg);
protected:
- void displayMsg(Message::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None);
+ void displayMsg(Message::Type, QString target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None);
void putCmd(const QString &cmd, const QByteArray ¶m, const QByteArray &prefix = QByteArray());
virtual void handle(const QString &member, QGenericArgument val0 = QGenericArgument(0),
return instance()->_storage->getBufferInfo(user, bufferId);
}
- //! Store a Message in the backlog.
+ //! Store a Message in the storage backend and set it's unique Id.
/** \note This method is threadsafe.
*
- * \param msg The message object to be stored
- * \return The globally unique id for the stored message
+ * \param message The message object to be stored
+ * \return true on success
*/
- static inline MsgId storeMessage(const Message &message) {
+ static inline bool storeMessage(Message &message) {
return instance()->_storage->logMessage(message);
}
+ //! Store a list of Messages in the storage backend and set their unique Id.
+ /** \note This method is threadsafe.
+ *
+ * \param messages The list message objects to be stored
+ * \return true on success
+ */
+ static inline bool storeMessages(MessageList &messages) {
+ return instance()->_storage->logMessages(messages);
+ }
+
//! Request a certain number messages stored in a given buffer.
/** \param buffer The buffer we request messages from
* \param first if != -1 return only messages with a MsgId >= first
_previousConnectionAttemptFailed = true;
qWarning() << qPrintable(tr("Could not connect to %1 (%2)").arg(networkName(), socket.errorString()));
emit connectionError(socket.errorString());
- emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Connection failure: %1").arg(socket.errorString()));
+ displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Connection failure: %1").arg(socket.errorString()));
emitConnectionError(socket.errorString());
if(socket.state() < QAbstractSocket::ConnectedState) {
socketDisconnected();
IrcUser *me_ = me();
if(me_) {
foreach(QString channel, me_->channels())
- emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, _quitReason, me_->hostmask());
+ displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, _quitReason, me_->hostmask());
}
setConnected(false);
inline void resetPingTimeout() { _lastPingTime = 0; }
+ inline void displayMsg(Message::Type msgType, BufferInfo::Type bufferType, const QString &target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None) {
+ emit displayMsg(networkId(), msgType, bufferType, target, text, sender, flags);
+ }
+
signals:
void recvRawServerMsg(QString);
void displayStatusMsg(QString);
- void displayMsg(Message::Type, BufferInfo::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None);
+ void displayMsg(NetworkId, Message::Type, BufferInfo::Type, const QString &target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None);
void disconnected(NetworkId networkId);
void connectionError(const QString &errorMsg);
#include "coreusersettings.h"
#include "logger.h"
+class ProcessMessagesEvent : public QEvent {
+public:
+ ProcessMessagesEvent() : QEvent(QEvent::User) {}
+};
+
CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent)
: QObject(parent),
_user(uid),
_bufferViewManager(new CoreBufferViewManager(_signalProxy, this)),
_ircListHelper(new CoreIrcListHelper(this)),
_coreInfo(this),
- scriptEngine(new QScriptEngine(this))
+ scriptEngine(new QScriptEngine(this)),
+ _processMessages(false)
{
SignalProxy *p = signalProxy();
connect(p, SIGNAL(peerRemoved(QIODevice *)), this, SLOT(removeClient(QIODevice *)));
// ALL messages coming pass through these functions before going to the GUI.
// So this is the perfect place for storing the backlog and log stuff.
-void CoreSession::recvMessageFromServer(Message::Type type, BufferInfo::Type bufferType,
- QString target, QString text, QString sender, Message::Flags flags) {
- CoreNetwork *net = qobject_cast<CoreNetwork*>(this->sender());
- Q_ASSERT(net);
-
- BufferInfo bufferInfo = Core::bufferInfo(user(), net->networkId(), bufferType, target);
- Message msg(bufferInfo, type, text, sender, flags);
- msg.setMsgId(Core::storeMessage(msg));
- Q_ASSERT(msg.msgId() != 0);
- emit displayMsg(msg);
+void CoreSession::recvMessageFromServer(NetworkId networkId, Message::Type type, BufferInfo::Type bufferType,
+ const QString &target, const QString &text, const QString &sender, Message::Flags flags) {
+ _messageQueue << RawMessage(networkId, type, bufferType, target, text, sender, flags);
+ if(!_processMessages) {
+ _processMessages = true;
+ QCoreApplication::postEvent(this, new ProcessMessagesEvent());
+ }
}
void CoreSession::recvStatusMsgFromServer(QString msg) {
return Core::requestBuffers(user());
}
+void CoreSession::customEvent(QEvent *event) {
+ if(event->type() != QEvent::User)
+ return;
+
+ processMessages();
+ event->accept();
+}
+
+void CoreSession::processMessages() {
+ qDebug() << "processing" << _messageQueue.count() << "messages..";
+ if(_messageQueue.count() == 1) {
+ const RawMessage &rawMsg = _messageQueue.first();
+ BufferInfo bufferInfo = Core::bufferInfo(user(), rawMsg.networkId, rawMsg.bufferType, rawMsg.target);
+ Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, rawMsg.flags);
+ Core::storeMessage(msg);
+ emit displayMsg(msg);
+ } else {
+ QHash<NetworkId, QHash<QString, BufferInfo> > bufferInfoCache;
+ MessageList messages;
+ BufferInfo bufferInfo;
+ for(int i = 0; i < _messageQueue.count(); i++) {
+ const RawMessage &rawMsg = _messageQueue.at(i);
+ if(bufferInfoCache.contains(rawMsg.networkId) && bufferInfoCache[rawMsg.networkId].contains(rawMsg.target)) {
+ bufferInfo = bufferInfoCache[rawMsg.networkId][rawMsg.target];
+ } else {
+ bufferInfo = Core::bufferInfo(user(), rawMsg.networkId, rawMsg.bufferType, rawMsg.target);
+ bufferInfoCache[rawMsg.networkId][rawMsg.target] = bufferInfo;
+ }
+ messages << Message(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, rawMsg.flags);
+ }
+
+ Core::storeMessages(messages);
+ // FIXME: extend protocol to a displayMessages(MessageList)
+ for(int i = 0; i < messages.count(); i++) {
+ emit displayMsg(messages[i]);
+ }
+ }
+ _processMessages = false;
+ _messageQueue.clear();
+}
QVariant CoreSession::sessionState() {
QVariantMap v;
id = info.networkId.toInt();
if(!_networks.contains(id)) {
CoreNetwork *net = new CoreNetwork(id, this);
- connect(net, SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)),
- this, SLOT(recvMessageFromServer(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)));
+ connect(net, SIGNAL(displayMsg(NetworkId, Message::Type, BufferInfo::Type, const QString &, const QString &, const QString &, Message::Flags)),
+ this, SLOT(recvMessageFromServer(NetworkId, Message::Type, BufferInfo::Type, const QString &, const QString &, const QString &, Message::Flags)));
connect(net, SIGNAL(displayStatusMsg(QString)), this, SLOT(recvStatusMsgFromServer(QString)));
net->setNetworkInfo(info);
#include "corecoreinfo.h"
#include "corealiasmanager.h"
#include "message.h"
+#include "storage.h"
class CoreBacklogManager;
class CoreBufferSyncer;
void removeClient(QIODevice *dev);
void recvStatusMsgFromServer(QString msg);
- void recvMessageFromServer(Message::Type, BufferInfo::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None);
+ void recvMessageFromServer(NetworkId networkId, Message::Type, BufferInfo::Type, const QString &target, const QString &text, const QString &sender = "", Message::Flags flags = Message::None);
void destroyNetwork(NetworkId);
void updateIdentityBySender();
+protected:
+ virtual void customEvent(QEvent *event);
+
private:
void loadSettings();
void initScriptEngine();
+ void processMessages();
UserId _user;
QScriptEngine *scriptEngine;
+ struct RawMessage {
+ NetworkId networkId;
+ Message::Type type;
+ BufferInfo::Type bufferType;
+ QString target;
+ QString text;
+ QString sender;
+ Message::Flags flags;
+ RawMessage(NetworkId networkId, Message::Type type, BufferInfo::Type bufferType, const QString &target, const QString &text, const QString &sender, Message::Flags flags)
+ : networkId(networkId), type(type), bufferType(bufferType), target(target), text(text), sender(sender), flags(flags) {}
+ };
+ QList<RawMessage> _messageQueue;
+ bool _processMessages;
};
#endif
return lastSeenHash;
}
-MsgId PostgreSqlStorage::logMessage(Message msg) {
+bool PostgreSqlStorage::logMessage(Message &msg) {
QSqlDatabase db = logDb();
if(!db.transaction()) {
qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!";
return false;
}
- QSqlQuery logMessageQuery(db);
- logMessageQuery.prepare(queryString("insert_message"));
- logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
- logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
- logMessageQuery.bindValue(":type", msg.type());
- logMessageQuery.bindValue(":flags", (int)msg.flags());
- logMessageQuery.bindValue(":sender", msg.sender());
- logMessageQuery.bindValue(":message", msg.contents());
- safeExec(logMessageQuery);
+ if(!prepareQuery("insert_message", queryString("insert_message"), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_message");
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return false;
+ }
+
+ QVariantList params;
+ params << msg.timestamp().toTime_t()
+ << msg.bufferInfo().bufferId().toInt()
+ << msg.type()
+ << (int)msg.flags()
+ << msg.sender()
+ << msg.contents();
+ QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db);
if(logMessageQuery.lastError().isValid()) {
// first we need to reset the transaction
db.rollback();
db.transaction();
- QSqlQuery addSenderQuery(db);
- addSenderQuery.prepare(queryString("insert_sender"));
- addSenderQuery.bindValue(":sender", msg.sender());
- safeExec(addSenderQuery);
- safeExec(logMessageQuery);
+ // it's possible that the sender was already added by another thread
+ // since the insert might fail we're setting a savepoint
+ savePoint("sender_sp1", db);
+ QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", msg.sender(), db);
+ if(addSenderQuery.lastError().isValid())
+ rollbackSavePoint("sender_sp1", db);
+ else
+ releaseSavePoint("sender_sp1", db);
+
+ logMessageQuery = db.exec(logMessageQuery.lastQuery());
if(!watchQuery(logMessageQuery)) {
+ qDebug() << "==================== Sender Query:";
+ watchQuery(addSenderQuery);
+ qDebug() << "==================== /Sender Query";
db.rollback();
- return MsgId();
+ return false;
}
}
logMessageQuery.first();
MsgId msgId = logMessageQuery.value(0).toInt();
db.commit();
+ if(msgId.isValid()) {
+ msg.setMsgId(msgId);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool PostgreSqlStorage::logMessages(MessageList &msgs) {
+ QSqlDatabase db = logDb();
+ if(!db.transaction()) {
+ qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!";
+ qWarning() << " -" << qPrintable(db.lastError().text());
+ return false;
+ }
+
+ if(!prepareQuery("insert_sender", queryString("insert_sender"), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_sender");
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return false;
+ }
+ QSet<QString> senders;
+ for(int i = 0; i < msgs.count(); i++) {
+ const QString &sender = msgs.at(i).sender();
+ if(senders.contains(sender))
+ continue;
+ senders << sender;
+
+ savePoint("sender_sp", db);
+ QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", sender, db);
+ if(addSenderQuery.lastError().isValid())
+ rollbackSavePoint("sender_sp", db);
+ else
+ releaseSavePoint("sender_sp", db);
+ }
+
+ // yes we loop twice over the same list. This avoids alternating queries.
+ if(!prepareQuery("insert_message", queryString("insert_message"), db)) {
+ qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_message");
+ qWarning() << " Error:" << db.lastError().text();
+ db.rollback();
+ return false;
+ }
+ bool error = false;
+ for(int i = 0; i < msgs.count(); i++) {
+ Message &msg = msgs[i];
+ QVariantList params;
+ params << msg.timestamp().toTime_t()
+ << msg.bufferInfo().bufferId().toInt()
+ << msg.type()
+ << (int)msg.flags()
+ << msg.sender()
+ << msg.contents();
+ QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db);
+ if(!watchQuery(logMessageQuery)) {
+ db.rollback();
+ error = true;
+ break;
+ } else {
+ logMessageQuery.first();
+ msg.setMsgId(logMessageQuery.value(0).toInt());
+ }
+ }
+
+ if(error) {
+ // we had a rollback in the db so we need to reset all msgIds
+ for(int i = 0; i < msgs.count(); i++) {
+ msgs[i].setMsgId(MsgId());
+ }
+ return false;
+ }
- Q_ASSERT(msgId.isValid());
- return msgId;
+ db.commit();
+ return true;
}
QList<Message> PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
QSqlQuery query = db.exec("BEGIN TRANSACTION READ ONLY");
return !query.lastError().isValid();
}
+
+bool PostgreSqlStorage::prepareQuery(const QString &handle, const QString &query, const QSqlDatabase &db) {
+ if(_preparedQueries.contains(db.connectionName()) && _preparedQueries[db.connectionName()].contains(handle))
+ return true; // already prepared
+
+ QMutexLocker locker(&_queryHashMutex);
+
+ static unsigned int stmtCount = 0;
+ QString queryId = QLatin1String("quassel_") + QString::number(++stmtCount, 16);
+ // qDebug() << "prepare:" << QString("PREPARE %1 AS %2").arg(queryId).arg(query);
+ db.exec(QString("PREPARE %1 AS %2").arg(queryId).arg(query));
+ if(db.lastError().isValid()) {
+ return false;
+ } else {
+ _preparedQueries[db.connectionName()][handle] = queryId;
+ return true;
+ }
+}
+
+QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &handle, const QVariantList ¶ms, const QSqlDatabase &db) {
+ if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
+ qWarning() << "PostgreSqlStorage::executePreparedQuery() no prepared Query with handle" << handle << "on Database" << db.connectionName();
+ return QSqlQuery();
+ }
+
+ QSqlDriver *driver = db.driver();
+
+ QStringList paramStrings;
+ QSqlField field;
+ for(int i = 0; i < params.count(); i++) {
+ const QVariant &value = params.at(i);
+ field.setType(value.type());
+ if(value.isNull())
+ field.clear();
+ else
+ field.setValue(value);
+
+ paramStrings << driver->formatValue(field);
+ }
+
+ const QString &queryId = _preparedQueries[db.connectionName()][handle];
+ if(params.isEmpty()) {
+ return db.exec(QString("EXECUTE %1").arg(queryId));
+ } else {
+ // qDebug() << "preparedExec:" << QString("EXECUTE %1 (%2)").arg(queryId).arg(paramStrings.join(", "));
+ return db.exec(QString("EXECUTE %1 (%2)").arg(queryId).arg(paramStrings.join(", ")));
+ }
+}
+
+QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &handle, const QVariant ¶m, const QSqlDatabase &db) {
+ if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
+ qWarning() << "PostgreSqlStorage::executePreparedQuery() no prepared Query with handle" << handle << "on Database" << db.connectionName();
+ return QSqlQuery();
+ }
+
+ QSqlField field;
+ field.setType(param.type());
+ if(param.isNull())
+ field.clear();
+ else
+ field.setValue(param);
+
+ const QString &queryId = _preparedQueries[db.connectionName()][handle];
+ QString paramString = db.driver()->formatValue(field);
+
+ // qDebug() << "preparedExec:" << QString("EXECUTE %1 (%2)").arg(queryId).arg(paramString);
+ return db.exec(QString("EXECUTE %1 (%2)").arg(queryId).arg(paramString));
+}
+
+void PostgreSqlStorage::deallocateQuery(const QString &handle, const QSqlDatabase &db) {
+ if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
+ return;
+ }
+ QMutexLocker locker(&_queryHashMutex);
+ QString queryId = _preparedQueries[db.connectionName()].take(handle);
+ db.exec(QString("DEALLOCATE %1").arg(queryId));
+}
+
QString displayName() const;
QString description() const;
QVariantMap setupKeys() const;
-
+
// TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc
/* User handling */
virtual void setAwayMessage(UserId user, NetworkId networkId, const QString &awayMsg);
virtual QString userModes(UserId user, NetworkId networkId);
virtual void setUserModes(UserId user, NetworkId networkId, const QString &userModes);
-
+
/* Buffer handling */
virtual BufferInfo bufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer = "", bool create = true);
virtual BufferInfo getBufferInfo(UserId user, const BufferId &bufferId);
virtual QHash<BufferId, MsgId> bufferLastSeenMsgIds(UserId user);
/* Message handling */
-
- virtual MsgId logMessage(Message msg);
+ virtual bool logMessage(Message &msg);
+ virtual bool logMessages(MessageList &msgs);
virtual QList<Message> requestMsgs(UserId user, BufferId bufferId, MsgId first = -1, MsgId last = -1, int limit = -1);
virtual QList<Message> requestAllMsgs(UserId user, MsgId first = -1, MsgId last = -1, int limit = -1);
virtual bool updateSchemaVersion(int newVersion);
virtual bool setupSchemaVersion(int version);
void safeExec(QSqlQuery &query);
+
bool beginReadOnlyTransaction(QSqlDatabase &db);
+ bool prepareQuery(const QString &handle, const QString &query, const QSqlDatabase &db);
+ QSqlQuery executePreparedQuery(const QString &handle, const QVariantList ¶ms, const QSqlDatabase &db);
+ QSqlQuery executePreparedQuery(const QString &handle, const QVariant ¶m, const QSqlDatabase &db);
+ void deallocateQuery(const QString &handle, const QSqlDatabase &db);
+
+ inline void savePoint(const QString &handle, const QSqlDatabase &db) { db.exec(QString("SAVEPOINT %1").arg(handle)); }
+ inline void rollbackSavePoint(const QString &handle, const QSqlDatabase &db) { db.exec(QString("ROLLBACK TO SAVEPOINT %1").arg(handle)); }
+ inline void releaseSavePoint(const QString &handle, const QSqlDatabase &db) { db.exec(QString("RELEASE SAVEPOINT %1").arg(handle)); }
+
private:
void bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info);
void bindServerInfo(QSqlQuery &query, const Network::Server &server);
QString _databaseName;
QString _userName;
QString _password;
-
+
static int _maxRetryCount;
+
+ typedef QHash<QString, QString> QueryHash;
+ QHash<QString, QueryHash> _preparedQueries; // one query hash per db connection
+ QMutex _queryHashMutex;
+
};
inline void PostgreSqlStorage::safeExec(QSqlQuery &query) { query.exec(); }
return lastSeenHash;
}
-MsgId SqliteStorage::logMessage(Message msg) {
+bool SqliteStorage::logMessage(Message &msg) {
QSqlQuery logMessageQuery(logDb());
logMessageQuery.prepare(queryString("insert_message"));
safeExec(addSenderQuery);
safeExec(logMessageQuery);
if(!watchQuery(logMessageQuery))
- return 0;
+ return false;
} else {
watchQuery(logMessageQuery);
}
}
MsgId msgId = logMessageQuery.lastInsertId().toInt();
- Q_ASSERT(msgId.isValid());
- return msgId;
+ if(msgId.isValid()) {
+ msg.setMsgId(msgId);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool SqliteStorage::logMessages(MessageList &msgs) {
+ // FIXME: optimize!
+ for(int i = 0; i < msgs.count(); i++) {
+ if(!logMessage(msgs[i]))
+ return false;
+ }
+ return true;
}
QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
virtual QHash<BufferId, MsgId> bufferLastSeenMsgIds(UserId user);
/* Message handling */
-
- virtual MsgId logMessage(Message msg);
+ virtual bool logMessage(Message &msg);
+ virtual bool logMessages(MessageList &msgs);
virtual QList<Message> requestMsgs(UserId user, BufferId bufferId, MsgId first = -1, MsgId last = -1, int limit = -1);
virtual QList<Message> requestAllMsgs(UserId user, MsgId first = -1, MsgId last = -1, int limit = -1);
/* Message handling */
- //! Store a Message in the backlog.
+ //! Store a Message in the storage backend and set its unique Id.
/** \param msg The message object to be stored
- * \return The globally unique id for the stored message
+ * \return true on success
*/
- virtual MsgId logMessage(Message msg) = 0;
+ virtual bool logMessage(Message &msg) = 0;
+
+ //! Store a list of Messages in the storage backend and set their unique Id.
+ /** \param msgs The list message objects to be stored
+ * \return true on success
+ */
+ virtual bool logMessages(MessageList &msgs) = 0;
//! Request a certain number messages stored in a given buffer.
/** \param buffer The buffer we request messages from