From 7a2708fff6cff5329002a3a16da266be51f1a3a1 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Fri, 18 May 2007 15:14:30 +0000 Subject: [PATCH] Added the baseclass for the upcoming storage backends. --- core/storage.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++ core/storage.h | 167 +++++++++++++++++++++++++++++++++++++++++++++++ main/global.h | 3 + 3 files changed, 328 insertions(+) create mode 100644 core/storage.cpp create mode 100644 core/storage.h diff --git a/core/storage.cpp b/core/storage.cpp new file mode 100644 index 00000000..982415a7 --- /dev/null +++ b/core/storage.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2005-07 by The Quassel Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "storage.h" + + +// OBSOLETE +// This is kept here for importing the old file-based backlog. + +/* This is a sample! + +void Storage::importOldBacklog() { + qDebug() << "Deleting backlog database..."; + logDb.exec(QString("DELETE FROM 'Backlog$%1$' WHERE SenderId != '$VERSION$'").arg(user)); + logDb.exec(QString("DELETE FROM 'Senders$%1$'").arg(user)); + logDb.exec(QString("DELETE FROM 'Buffers$%1$'").arg(user)); + nextMsgId = 1; nextBufferId = 1; nextSenderId = 1; + qDebug() << "Importing old backlog files..."; + initBackLogOld(); + if(!backLogEnabledOld) return; + logDb.exec("VACUUM"); + qDebug() << "Backlog successfully imported, you have to restart Quassel now!"; + exit(0); + +} +*/ + +// file name scheme: quassel-backlog-2006-29-10.bin +void Storage::initBackLogOld() { + backLogDir = QDir(Global::quasselDir + "/backlog"); + if(!backLogDir.exists()) { + qWarning(QString("Creating backlog directory \"%1\"...").arg(backLogDir.absolutePath()).toAscii()); + if(!backLogDir.mkpath(backLogDir.absolutePath())) { + qWarning(QString("Could not create backlog directory! Disabling logging...").toAscii()); + backLogEnabledOld = false; + return; + } + } + backLogDir.refresh(); + //if(!backLogDir.isReadable()) { + // qWarning(QString("Cannot read directory \"%1\". Disabling logging...").arg(backLogDir.absolutePath()).toAscii()); + // backLogEnabled = false; + // return; + //} + QStringList networks = backLogDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable, QDir::Name); + foreach(QString net, networks) { + QDir dir(backLogDir.absolutePath() + "/" + net); + if(!dir.exists()) { + qWarning(QString("Could not change to directory \"%1\"!").arg(dir.absolutePath()).toAscii()); + continue; + } + QStringList logs = dir.entryList(QStringList("quassel-backlog-*.bin"), QDir::Files|QDir::Readable, QDir::Name); + foreach(QString name, logs) { + QFile f(dir.absolutePath() + "/" + name); + if(!f.open(QIODevice::ReadOnly)) { + qWarning(QString("Could not open \"%1\" for reading!").arg(f.fileName()).toAscii()); + continue; + } + QDataStream in(&f); + in.setVersion(QDataStream::Qt_4_2); + QByteArray verstring; quint8 vernum; in >> verstring >> vernum; + if(verstring != BACKLOG_STRING) { + qWarning(QString("\"%1\" is not a Quassel backlog file!").arg(f.fileName()).toAscii()); + f.close(); continue; + } + if(vernum != BACKLOG_FORMAT) { + qWarning(QString("\"%1\": Version mismatch!").arg(f.fileName()).toAscii()); + f.close(); continue; + } + qDebug() << "Reading backlog from" << f.fileName(); + logFileDates[net] = QDate::fromString(f.fileName(), + QString("'%1/quassel-backlog-'yyyy-MM-dd'.bin'").arg(dir.absolutePath())); + if(!logFileDates[net].isValid()) { + qWarning(QString("\"%1\" has an invalid file name!").arg(f.fileName()).toAscii()); + } + while(!in.atEnd()) { + quint8 t, f; + quint32 ts; + QByteArray s, m, targ; + in >> ts >> t >> f >> targ >> s >> m; + QString target = QString::fromUtf8(targ); + QString sender = QString::fromUtf8(s); + QString text = QString::fromUtf8(m); + BufferId id; + if((f & Message::PrivMsg) && !(f & Message::Self)) { + id = getBufferId(net, sender); + } else { + id = getBufferId(net, target); + } + Message msg(QDateTime::fromTime_t(ts), id, (Message::Type)t, text, sender, f); + //backLog[net].append(m); + logMessage(msg); + } + f.close(); + } + } + backLogEnabledOld = true; +} + + +/** Log a core message (emitted via a displayMsg() signal) to the backlog file. + * If a file for the current day does not exist, one will be created. Otherwise, messages will be appended. + * The file header is the string defined by BACKLOG_STRING, followed by a quint8 specifying the format + * version (BACKLOG_FORMAT). The rest is simply serialized Message objects. + */ +void Storage::logMessageOld(QString net, Message msg) { + backLog[net].append(msg); + if(!logFileDirs.contains(net)) { + QDir dir(backLogDir.absolutePath() + "/" + net); + if(!dir.exists()) { + qWarning(QString("Creating backlog directory \"%1\"...").arg(dir.absolutePath()).toAscii()); + if(!dir.mkpath(dir.absolutePath())) { + qWarning(QString("Could not create backlog directory!").toAscii()); + return; + } + } + logFileDirs[net] = dir; + Q_ASSERT(!logFiles.contains(net) && !logStreams.contains(net)); + if(!logFiles.contains(net)) logFiles[net] = new QFile(); + if(!logStreams.contains(net)) logStreams[net] = new QDataStream(); + } + if(!logFileDates[net].isValid() || logFileDates[net] < QDate::currentDate()) { + if(logFiles[net]->isOpen()) logFiles[net]->close(); + logFileDates[net] = QDate::currentDate(); + } + if(!logFiles[net]->isOpen()) { + logFiles[net]->setFileName(QString("%1/%2").arg(logFileDirs[net].absolutePath()) + .arg(logFileDates[net].toString("'quassel-backlog-'yyyy-MM-dd'.bin'"))); + if(!logFiles[net]->open(QIODevice::WriteOnly|QIODevice::Append|QIODevice::Unbuffered)) { + qWarning(QString("Could not open \"%1\" for writing: %2") + .arg(logFiles[net]->fileName()).arg(logFiles[net]->errorString()).toAscii()); + return; + } + logStreams[net]->setDevice(logFiles[net]); logStreams[net]->setVersion(QDataStream::Qt_4_2); + if(!logFiles[net]->size()) *logStreams[net] << BACKLOG_STRING << (quint8)BACKLOG_FORMAT; + } + *logStreams[net] << msg; +} + + + diff --git a/core/storage.h b/core/storage.h new file mode 100644 index 00000000..34ca0a88 --- /dev/null +++ b/core/storage.h @@ -0,0 +1,167 @@ +/*************************************************************************** + * Copyright (C) 2005-07 by The Quassel Team * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef _STORAGE_H_ +#define _STORAGE_H_ + +#include + +#include "global.h" + +class Storage : public QObject { + Q_OBJECT + + public: + Storage() {}; + virtual ~Storage() {}; + + //! Initialize the static parts of the storage class + /** This is called by the core before any other method of the storage backend is used. + * This should be used to perform any static initialization that might be necessary. + * DO NOT use this for creating database connection or similar stuff, since init() might be + * called even if the storage backend is never be actually used (because no user selected it). + * For anything like this, the constructor (which is called if and when we actually create an instance + * of the storage backend) is the right place. + */ + virtual static void init() {}; + + /* General */ + + //! Check if the storage type is available. + /** A storage subclass should return true if it can be successfully used, i.e. if all + * prerequisites are in place (e.g. we have an appropriate DB driver etc.). + * \return True if and only if the storage class can be successfully used. + */ + virtual static bool isAvailable() = 0; + + //! Returns the display name of the storage backend + /** \return A string that can be used by the GUI to describe the storage backend */ + virtual static QString displayName() = 0; + + // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc + + /* User handling */ + + //! Add a new core user to the storage. + /** \param user The username of the new user + * \param password The cleartext password for the new user + * \return The new user's UserId + */ + virtual UserId addUser(QString user, QString password) = 0; + + //! Update a core user's password. + /** \param user The user's name + * \param password The user's new password + */ + virtual void updateUser(QString user, QString password) = 0; + + //! Validate a username with a given password. + /** \param user The username to validate + * \param password The user's alleged password + * \return A valid UserId if the password matches the username; 0 else + */ + virtual UserId validateUser(QString user, QString password) = 0; + + //! Remove a core user from storage. + /** \param user The username to delete + */ + virtual void delUser(QString user) = 0; + + /* Buffer handling */ + + //! Get the unique BufferId for the given combination of network and buffername for a user. + /** \param user The core user who owns this buffername + * \param network The network name + * \param buffer The buffer name (if empty, the net's status buffer is returned) + * \return The BufferId corresponding to the given network and buffer name, or 0 if not found + */ + virtual BufferId getBufferId(UserId user, QString network, QString buffer = "") = 0; + + //! Request a list of all buffers known to a user since a certain point in time. + /** This method is used to get a list of all buffers we have stored a backlog from. + * Optionally, a QDateTime can be given, so that only buffers are listed that where active + * since that point in time. + * \param user The user whose buffers we request + * \param since If this is defined, older buffers will be ignored + * \return A list of the BufferIds for all buffers as requested + */ + virtual QList requestBuffers(UserId user, QDateTime since = QDateTime()) = 0; + + /* Message handling */ + + //! Store a Message in the backlog. + /** \param msg The message object to be stored + * \return The globally uniqe id for the stored message + */ + virtual MsgId logMessage(Message msg) = 0; + + //! Request a certain number (or all) messages stored in a given buffer. + /** \param buffer The buffer we request messages from + * \param lastmsgs The number of messages we would like to receive, or -1 if we'd like all messages from that buffername + * \param offset Do not return (but DO count) messages with MsgId >= offset, if offset >= 0 + * \return The requested list of messages + */ + virtual QList requestMsgs(BufferId buffer, int lastmsgs = -1, int offset = -1) = 0; + + //! Request messages stored in a given buffer since a certain point in time. + /** \param buffer The buffer we request messages from + * \param since Only return messages newer than this point in time + * \param offset Do not return messages with MsgId >= offset, if offset >= 0 + * \return The requested list of messages + */ + virtual QList requestMsgs(BufferId buffer, QDateTime since, int offset = -1) = 0; + + //! Request a range of messages stored in a given buffer. + /** \param buffer The buffer we request messages from + * \param first Return messages with first <= MsgId <= last + * \param last Return messages with first <= MsgId <= last + * \return The requested list of messages + */ + virtual QList requestMsgRange(BufferId buffer, int first, int last) = 0; + + public slots: + //! This is just for importing the old file-based backlog */ + /** This slot needs to be implemented in the storage backends. + * It should first prepare (delete?) the database, then call initBackLogOld(). + * If the importing was successful, backLogEnabledOld will be true afterwards. + */ + void importOldBacklog() = 0; + + signals: + //! Sent if a new BufferId is created, or an existing one changed somehow. + void bufferIdUpdated(BufferId); + + protected: + // Old stuff, just for importing old file-based data + void initBackLogOld(); + void logMessageOld(QString net, Message); + + bool backLogEnabledOld; + QDir backLogDir; + QHash > backLog; + QHash logFiles; + QHash logStreams; + QHash logFileDates; + QHash logFileDirs; + +}; + + +#endif \ No newline at end of file diff --git a/main/global.h b/main/global.h index 8787eea3..a662fd77 100644 --- a/main/global.h +++ b/main/global.h @@ -36,6 +36,9 @@ class Global; typedef QMap VarMap; extern Global *global; +typedef uint UserId; +typedef uint MsgId; + /** * This class is mostly a globally synchronized data store, meant for storing systemwide settings such * as identities or network lists. This class is a singleton, but not static as we'd like to use signals and -- 2.20.1