The Core Configuration Wizard is back! teH rul!
authorManuel Nickschas <sputnick@quassel-irc.org>
Fri, 8 Feb 2008 00:56:11 +0000 (00:56 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Fri, 8 Feb 2008 00:56:11 +0000 (00:56 +0000)
Well, probably still has some rough edges, but you should be able to configure
a Quassel Core from scratch now again. If you want to test this, be sure to remove both
the database and the settings (.quassel/* and .config/Quassel Project/* on Linux).
Oh, and I completely broke the client/core protocol again. Oh well.

26 files changed:
Quassel.kdevelop.filelist
src/client/clientsyncer.cpp
src/client/clientsyncer.h
src/core/abstractsqlstorage.cpp
src/core/abstractsqlstorage.h
src/core/core.cpp
src/core/core.h
src/core/coresettings.cpp
src/core/coresettings.h
src/core/sqlitestorage.cpp
src/core/sqlitestorage.h
src/core/storage.h
src/qtui/coreconfigwizard.cpp [new file with mode: 0644]
src/qtui/coreconfigwizard.h [new file with mode: 0644]
src/qtui/coreconnectdlg.cpp
src/qtui/coreconnectdlg.h
src/qtui/inputwidget.h
src/qtui/qtui.pri
src/qtui/settingsdlg.cpp
src/qtui/ui/coreconfigwizardadminuserpage.ui [new file with mode: 0644]
src/qtui/ui/coreconfigwizardintropage.ui [new file with mode: 0644]
src/qtui/ui/coreconfigwizardstorageselectionpage.ui [new file with mode: 0644]
src/qtui/ui/coreconfigwizardsyncpage.ui [new file with mode: 0644]
src/qtui/ui/coreconnectdlg.ui
src/uisupport/uisettings.h
version.inc

index 19f4d2e..b6626fa 100644 (file)
@@ -154,12 +154,12 @@ src/qtui/chatwidget.cpp
 src/qtui/chatwidget.h
 src/qtui/configwizard.cpp
 src/qtui/configwizard.h
+src/qtui/coreconfigwizard.cpp
+src/qtui/coreconfigwizard.h
 src/qtui/coreconnectdlg.cpp
 src/qtui/coreconnectdlg.h
 src/qtui/debugconsole.cpp
 src/qtui/debugconsole.h
-src/qtui/guisettings.cpp
-src/qtui/guisettings.h
 src/qtui/inputwidget.cpp
 src/qtui/inputwidget.h
 src/qtui/mainwin.cpp
index b8cf4b2..29b4973 100644 (file)
@@ -59,6 +59,10 @@ void ClientSyncer::coreHasData() {
       emit connectionError(msg["Error"].toString());
       disconnectFromCore();
       return;
+    } else if(msg["MsgType"] == "CoreSetupAck") {
+      emit coreSetupSuccess();
+    } else if(msg["MsgType"] == "CoreSetupReject") {
+      emit coreSetupFailed(msg["Error"].toString());
     } else if(msg["MsgType"] == "ClientLoginReject") {
       emit loginFailed(msg["Error"].toString());
     } else if(msg["MsgType"] == "ClientLoginAck") {
@@ -173,11 +177,21 @@ void ClientSyncer::clientInitAck(const QVariantMap &msg) {
     return;
   }
   emit connectionMsg(msg["CoreInfo"].toString());
-  if(msg["LoginEnabled"].toBool()) {
+  if(!msg["Configured"].toBool()) {
+    // start wizard
+    emit startCoreSetup(msg["StorageBackends"].toList());
+  } else if(msg["LoginEnabled"].toBool()) {
     emit startLogin();
   }
 }
 
+void ClientSyncer::doCoreSetup(const QVariant &setupData) {
+  QVariantMap setup;
+  setup["MsgType"] = "CoreSetupData";
+  setup["SetupData"] = setupData;
+  SignalProxy::writeDataToDevice(socket, setup);
+}
+
 void ClientSyncer::loginToCore(const QString &user, const QString &passwd) {
   emit connectionMsg(tr("Logging in..."));
   QVariantMap clientLogin;
index 9501d8d..95aee59 100644 (file)
@@ -51,6 +51,9 @@ class ClientSyncer : public QObject {
     void loginFailed(const QString &error);
     void loginSuccess();
     void syncFinished();
+    void startCoreSetup(const QVariantList &);
+    void coreSetupSuccess();
+    void coreSetupFailed(const QString &error);
 
 
   public slots:
@@ -79,6 +82,8 @@ class ClientSyncer : public QObject {
     void syncToCore(const QVariantMap &sessionState);
     void sessionStateReceived(const QVariantMap &state);
 
+    void doCoreSetup(const QVariant &setupData);
+
   private:
     QPointer<QIODevice> socket;
     quint32 blockSize;
index 99289f9..b20d1dc 100644 (file)
@@ -50,7 +50,7 @@ QSqlDatabase AbstractSqlStorage::logDb() {
     return db;
 
   if(!openDb()) {
-    qWarning() << "Unable to Open Database" << engineName();
+    qWarning() << "Unable to Open Database" << displayName();
     qWarning() << " -" << db.lastError().text();
   }
 
@@ -116,9 +116,9 @@ QString AbstractSqlStorage::queryString(const QString &queryName, int version) {
   if(version == 0)
     version = schemaVersion();
     
-  QFileInfo queryInfo(QString(":/SQL/%1/%2/%3.sql").arg(engineName()).arg(version).arg(queryName));
+  QFileInfo queryInfo(QString(":/SQL/%1/%2/%3.sql").arg(displayName()).arg(version).arg(queryName));
   if(!queryInfo.exists() || !queryInfo.isFile() || !queryInfo.isReadable()) {
-    qWarning() << "Unable to read SQL-Query" << queryName << "for Engine" << engineName();
+    qWarning() << "Unable to read SQL-Query" << queryName << "for engine" << displayName();
     return QString();
   }
 
@@ -151,7 +151,7 @@ QSqlQuery *AbstractSqlStorage::cachedQuery(const QString &queryName) {
 
 QStringList AbstractSqlStorage::setupQueries() {
   QStringList queries;
-  QDir dir = QDir(QString(":/SQL/%1/%2/").arg(engineName()).arg(schemaVersion()));
+  QDir dir = QDir(QString(":/SQL/%1/%2/").arg(displayName()).arg(schemaVersion()));
   foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "setup*", QDir::NoFilter, QDir::Name)) {
     queries << queryString(fileInfo.baseName());
   }
@@ -178,7 +178,7 @@ bool AbstractSqlStorage::setup(const QVariantMap &settings) {
 
 QStringList AbstractSqlStorage::upgradeQueries(int version) {
   QStringList queries;
-  QDir dir = QDir(QString(":/SQL/%1/%2/").arg(engineName()).arg(version));
+  QDir dir = QDir(QString(":/SQL/%1/%2/").arg(displayName()).arg(version));
   foreach(QFileInfo fileInfo, dir.entryInfoList(QStringList() << "upgrade*", QDir::NoFilter, QDir::Name)) {
     queries << queryString(fileInfo.baseName(), version);
   }
@@ -212,7 +212,7 @@ int AbstractSqlStorage::schemaVersion() {
 
   int version;
   bool ok;
-  QDir dir = QDir(":/SQL/" + engineName());
+  QDir dir = QDir(":/SQL/" + displayName());
   foreach(QFileInfo fileInfo, dir.entryInfoList()) {
     if(!fileInfo.isDir())
       continue;
index 6fefb78..a85fdbb 100644 (file)
@@ -34,10 +34,6 @@ public:
   AbstractSqlStorage(QObject *parent = 0);
   virtual ~AbstractSqlStorage();
 
-  //! Returns the name of the storage backend engine
-  /** \return A virtual equivalent of displayName() */
-  virtual QString engineName() { return ""; }
-  
 protected:
   bool init(const QVariantMap &settings = QVariantMap());
   virtual void sync();
index 2e3da6d..32b34f5 100644 (file)
@@ -45,13 +45,17 @@ void Core::destroy() {
   instanceptr = 0;
 }
 
-Core::Core()
-  : storage(0)
-{
+Core::Core() : storage(0) {
   startTime = QDateTime::currentDateTime();  // for uptime :)
 
-  connect(&_storageSyncTimer, SIGNAL(timeout()),
-         this, SLOT(syncStorage()));
+  // Register storage backends here!
+  registerStorageBackend(new SqliteStorage(this));
+
+  if(!_storageBackends.count()) {
+    qWarning() << qPrintable(tr("Could not initialize any storage backend! Exiting..."));
+    exit(1); // TODO make this less brutal (especially for mono client -> popup)
+  }
+  connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
   _storageSyncTimer.start(10 * 60 * 1000); // in msecs
 }
 
@@ -59,49 +63,46 @@ void Core::init() {
   configured = false;
 
   CoreSettings cs;
-  if(!(configured = initStorage(cs.databaseSettings().toMap()))) {
-    qWarning("Core is currently not configured!");
-  }
 
-  connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
-  startListening(cs.port());
-  guiUser = 0;
-
-}
+  // TODO migrate old db settings
 
-bool Core::initStorage(QVariantMap dbSettings, bool setup) {
-  QString engine = dbSettings["Type"].toString().toLower();
-
-  if(storage) {
-    qDebug() << "Deleting old storage object.";
-    storage->deleteLater();
-    storage = 0;
-  }
-
-  // FIXME register new storageProviders here
-  if(engine == "sqlite" && SqliteStorage::isAvailable()) {
-    storage = new SqliteStorage(this);
-    connect(storage, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)));
-  } else {
-    qWarning() << "Selected StorageBackend is not available:" << dbSettings["Type"].toString();
-    return configured = false;
-  }
+  if(!(configured = initStorage(cs.storageSettings().toMap()))) {
+    qWarning("Core is currently not configured!");
 
-  if(setup && !storage->setup(dbSettings)) {
-    return configured = false;
+    // try to migrate old settings
+    QVariantMap old = cs.oldDbSettings().toMap();
+    if(old.count() && old["Type"].toString() == "SQlite") {
+      QVariantMap newSettings;
+      newSettings["Backend"] = "SQLite";
+      if((configured = initStorage(newSettings))) {
+        qWarning("...but thankfully I found some old settings to migrate!");
+        cs.setStorageSettings(newSettings);
+      }
+    }
   }
 
-  return configured = storage->init(dbSettings);
+  connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
+  if(!startListening(cs.port())) exit(1); // TODO make this less brutal
 }
 
 Core::~Core() {
-  // FIXME properly shutdown the sessions
+  foreach(QTcpSocket *socket, blocksizes.keys()) {
+    socket->disconnectFromHost();  // disconnect local (i.e. non-authed) clients
+  }
   qDeleteAll(sessions);
+  qDeleteAll(_storageBackends);
 }
 
-void Core::syncStorage() {
-  QMutexLocker locker(&mutex);
-  return instance()->storage->sync();
+/*** Session Restore ***/
+
+void Core::saveState() {
+  CoreSettings s;
+  QVariantMap state;
+  QVariantList activeSessions;
+  foreach(UserId user, instance()->sessions.keys()) activeSessions << QVariant::fromValue<UserId>(user);
+  state["CoreBuild"] = Global::quasselBuild;
+  state["ActiveSessions"] = activeSessions;
+  s.setCoreState(state);
 }
 
 void Core::restoreState() {
@@ -122,18 +123,83 @@ void Core::restoreState() {
       UserId user = v.value<UserId>();
       instance()->createSession(user, true);
     }
-    qDebug() << "...done.";
   }
 }
 
-void Core::saveState() {
+/*** Core Setup ***/
+
+QString Core::setupCore(const QVariant &setupData_) {
+  QVariantMap setupData = setupData_.toMap();
+  QString user = setupData.take("AdminUser").toString();
+  QString password = setupData.take("AdminPasswd").toString();
+  if(user.isEmpty() || password.isEmpty()) {
+    return tr("Admin user or password not set.");
+  }
+  if(!initStorage(setupData, true)) {
+    return tr("Could not setup storage!");
+  }
   CoreSettings s;
-  QVariantMap state;
-  QVariantList activeSessions;
-  foreach(UserId user, instance()->sessions.keys()) activeSessions << QVariant::fromValue<UserId>(user);
-  state["CoreBuild"] = Global::quasselBuild;
-  state["ActiveSessions"] = activeSessions;
-  s.setCoreState(state);
+  //s.setStorageSettings(msg);
+  qDebug() << qPrintable(tr("Creating admin user..."));
+  mutex.lock();
+  storage->addUser(user, password);
+  mutex.unlock();
+  startListening();  // TODO check when we need this
+  return QString();
+}
+
+/*** Storage Handling ***/
+
+bool Core::registerStorageBackend(Storage *backend) {
+  if(backend->isAvailable()) {
+    _storageBackends[backend->displayName()] = backend;
+    return true;
+  } else {
+    backend->deleteLater();
+    return false;
+  }
+}
+
+void Core::unregisterStorageBackend(Storage *backend) {
+  _storageBackends.remove(backend->displayName());
+  backend->deleteLater();
+}
+
+// old db settings:
+// "Type" => "sqlite"
+bool Core::initStorage(QVariantMap dbSettings, bool setup) {
+  QString backend = dbSettings["Backend"].toString();
+  if(backend.isEmpty()) {
+    //qWarning() << "No storage backend selected!";
+    return configured = false;
+  }
+
+  if(_storageBackends.contains(backend)) {
+    storage = _storageBackends[backend];
+  } else {
+    qWarning() << "Selected storage backend is not available:" << backend;
+    return configured = false;
+  }
+  if(!storage->init(dbSettings)) {
+    if(!setup || !(storage->setup(dbSettings) && storage->init(dbSettings))) {
+      qWarning() << "Could not init storage!";
+      storage = 0;
+      return configured = false;
+    }
+  }
+  // delete all other backends
+  foreach(Storage *s, _storageBackends.values()) {
+    if(s != storage) s->deleteLater();
+  }
+  _storageBackends.clear();
+
+  connect(storage, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)), this, SIGNAL(bufferInfoUpdated(UserId, const BufferInfo &)));
+  return configured = true;
+}
+
+void Core::syncStorage() {
+  QMutexLocker locker(&mutex);
+  if(storage) storage->sync();
 }
 
 /*** Storage Access ***/
@@ -142,7 +208,7 @@ bool Core::createNetworkId(UserId user, NetworkInfo &info) {
   NetworkId networkId = instance()->storage->createNetworkId(user, info);
   if(!networkId.isValid())
     return false;
-  
+
   info.networkId = networkId;
   return true;
 }
@@ -186,7 +252,7 @@ QList<BufferInfo> Core::requestBuffers(UserId user, QDateTime since) {
 
 bool Core::startListening(uint port) {
   if(!server.listen(QHostAddress::Any, port)) {
-    qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii());
+    qWarning(qPrintable(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())));
     return false;
   }
   qDebug() << "Listening for GUI clients on port" << server.serverPort();
@@ -200,7 +266,7 @@ void Core::stopListening() {
 
 void Core::incomingConnection() {
   // TODO implement SSL
-  while (server.hasPendingConnections()) {
+  while(server.hasPendingConnections()) {
     QTcpSocket *socket = server.nextPendingConnection();
     connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
     connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
@@ -221,53 +287,85 @@ void Core::clientHasData() {
   QVariant item;
   while(SignalProxy::readDataFromDevice(socket, blocksizes[socket], item)) {
     QVariantMap msg = item.toMap();
-    if(!msg.contains("MsgType")) {
-      // Client is way too old, does not even use the current init format
-      qWarning() << qPrintable(tr("Antique client trying to connect... refusing."));
-      socket->close();
-      return;
+    processClientMessage(socket, msg);
+  }
+}
+
+void Core::processClientMessage(QTcpSocket *socket, const QVariantMap &msg) {
+  if(!msg.contains("MsgType")) {
+    // Client is way too old, does not even use the current init format
+    qWarning() << qPrintable(tr("Antique client trying to connect... refusing."));
+    socket->close();
+    return;
+  }
+  // OK, so we have at least an init message format we can understand
+  if(msg["MsgType"] == "ClientInit") {
+    QVariantMap reply;
+    reply["CoreVersion"] = Global::quasselVersion;
+    reply["CoreDate"] = Global::quasselDate;
+    reply["CoreBuild"] = Global::quasselBuild;
+    // TODO: Make the core info configurable
+    int uptime = startTime.secsTo(QDateTime::currentDateTime());
+    int updays = uptime / 86400; uptime %= 86400;
+    int uphours = uptime / 3600; uptime %= 3600;
+    int upmins = uptime / 60;
+    reply["CoreInfo"] = tr("<b>Quassel Core Version %1 (Build >= %2)</b><br>"
+                            "Up %3d%4h%5m (since %6)").arg(Global::quasselVersion).arg(Global::quasselBuild)
+                            .arg(updays).arg(uphours,2,10,QChar('0')).arg(upmins,2,10,QChar('0')).arg(startTime.toString(Qt::TextDate));
+
+    reply["SupportSsl"] = false;
+    reply["LoginEnabled"] = true;
+
+    // Just version information -- check it!
+    if(msg["ClientBuild"].toUInt() < Global::clientBuildNeeded) {
+      reply["MsgType"] = "ClientInitReject";
+      reply["Error"] = tr("<b>Your Quassel Client is too old!</b><br>"
+                          "This core needs at least client version %1 (Build >= %2).<br>"
+                          "Please consider upgrading your client.").arg(Global::quasselVersion).arg(Global::quasselBuild);
+      SignalProxy::writeDataToDevice(socket, reply);
+      qWarning() << qPrintable(tr("Client %1 too old, rejecting.").arg(socket->peerAddress().toString()));
+      socket->close(); return;
+    }
+    // check if we are configured, start wizard otherwise
+    if(!configured) {
+      reply["Configured"] = false;
+      QList<QVariant> backends;
+      foreach(Storage *backend, _storageBackends.values()) {
+        QVariantMap v;
+        v["DisplayName"] = backend->displayName();
+        v["Description"] = backend->description();
+        backends.append(v);
+      }
+      reply["StorageBackends"] = backends;
+      reply["LoginEnabled"] = false;
+    } else {
+      reply["Configured"] = true;
+    }
+    clientInfo[socket] = msg; // store for future reference
+    reply["MsgType"] = "ClientInitAck";
+    SignalProxy::writeDataToDevice(socket, reply);
+  } else {
+    // for the rest, we need an initialized connection
+    if(!clientInfo.contains(socket)) {
+      QVariantMap reply;
+      reply["MsgType"] = "ClientLoginReject";
+      reply["Error"] = tr("<b>Client not initialized!</b><br>You need to send an init message before trying to login.");
+      SignalProxy::writeDataToDevice(socket, reply);
+      qWarning() << qPrintable(tr("Client %1 did not send an init message before trying to login, rejecting.").arg(socket->peerAddress().toString()));
+      socket->close(); return;
     }
-    // OK, so we have at least an init message format we can understand
-    if(msg["MsgType"] == "ClientInit") {
+    if(msg["MsgType"] == "CoreSetupData") {
       QVariantMap reply;
-      reply["CoreVersion"] = Global::quasselVersion;
-      reply["CoreDate"] = Global::quasselDate;
-      reply["CoreBuild"] = Global::quasselBuild;
-      // TODO: Make the core info configurable
-      int uptime = startTime.secsTo(QDateTime::currentDateTime());
-      int updays = uptime / 86400; uptime %= 86400;
-      int uphours = uptime / 3600; uptime %= 3600;
-      int upmins = uptime / 60;
-      reply["CoreInfo"] = tr("<b>Quassel Core Version %1 (Build >= %2)</b><br>"
-                             "Up %3d%4h%5m (since %6)").arg(Global::quasselVersion).arg(Global::quasselBuild)
-                             .arg(updays).arg(uphours,2,10,QChar('0')).arg(upmins,2,10,QChar('0')).arg(startTime.toString(Qt::TextDate));
-
-      reply["SupportSsl"] = false;
-      reply["LoginEnabled"] = true;
-      // TODO: check if we are configured, start wizard otherwise
-
-      // Just version information -- check it!
-      if(msg["ClientBuild"].toUInt() < Global::clientBuildNeeded) {
-        reply["MsgType"] = "ClientInitReject";
-        reply["Error"] = tr("<b>Your Quassel Client is too old!</b><br>"
-                            "This core needs at least client version %1 (Build >= %2).<br>"
-                            "Please consider upgrading your client.").arg(Global::quasselVersion).arg(Global::quasselBuild);
-        SignalProxy::writeDataToDevice(socket, reply);
-        qWarning() << qPrintable(tr("Client %1 too old, rejecting.").arg(socket->peerAddress().toString()));
-        socket->close(); return;
+      QString result = setupCore(msg["SetupData"]);
+      if(!result.isEmpty()) {
+        reply["MsgType"] = "CoreSetupReject";
+        reply["Error"] = result;
+      } else {
+        reply["MsgType"] = "CoreSetupAck";
       }
-      clientInfo[socket] = msg; // store for future reference
-      reply["MsgType"] = "ClientInitAck";
       SignalProxy::writeDataToDevice(socket, reply);
     } else if(msg["MsgType"] == "ClientLogin") {
       QVariantMap reply;
-      if(!clientInfo.contains(socket)) {
-        reply["MsgType"] = "ClientLoginReject";
-        reply["Error"] = tr("<b>Client not initialized!</b><br>You need to send an init message before trying to login.");
-        SignalProxy::writeDataToDevice(socket, reply);
-        qWarning() << qPrintable(tr("Client %1 did not send an init message before trying to login, rejecting.").arg(socket->peerAddress().toString()));
-        socket->close(); return;
-      }
       mutex.lock();
       UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString());
       mutex.unlock();
@@ -275,48 +373,27 @@ void Core::clientHasData() {
         reply["MsgType"] = "ClientLoginReject";
         reply["Error"] = tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.");
         SignalProxy::writeDataToDevice(socket, reply);
-        continue;
+        return;
       }
       reply["MsgType"] = "ClientLoginAck";
       SignalProxy::writeDataToDevice(socket, reply);
       qDebug() << qPrintable(tr("Client %1 initialized and authentificated successfully as \"%2\".").arg(socket->peerAddress().toString(), msg["User"].toString()));
       setupClientSession(socket, uid);
     }
-    //socket->close(); return;
-    /*
-    // we need to auth the client
-    try {
-      QVariantMap msg = item.toMap();
-      if (msg["GuiProtocol"].toUInt() != GUI_PROTOCOL) {
-        throw Exception("GUI client version mismatch");
-      }
-      if (configured) {
-        processClientInit(socket, msg);
-      } else {
-        processCoreSetup(socket, msg);
-      }
-    } catch(Storage::AuthError) {
-      qWarning() << "Authentification error!";  // FIXME: send auth error to client
-      socket->close();
-      return;
-    } catch(Exception e) {
-      qWarning() << "Client init error:" << e.msg();
-      socket->close();
-      return;
-    } */
   }
 }
 
 // Potentially called during the initialization phase (before handing the connection off to the session)
 void Core::clientDisconnected() {
-  QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
+  QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());  // Note: This might be a QObject* already (if called by ~Core())!
+  Q_ASSERT(socket);
   blocksizes.remove(socket);
   clientInfo.remove(socket);
-  qDebug() << qPrintable(tr("Client %1 disconnected.").arg(socket->peerAddress().toString()));
+  qDebug() << qPrintable(tr("Non-authed client disconnected."));
   socket->deleteLater();
   socket = 0;
 
-  // make server listen again if still not configured  FIXME
+  // make server listen again if still not configured
   if (!configured) {
     startListening();
   }
@@ -325,6 +402,7 @@ void Core::clientDisconnected() {
   // Suggestion: kill sessions if they are not connected to any network and client.
 }
 
+/*
 void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) {
   if(msg["HasSettings"].toBool()) {
     QVariantMap auth;
@@ -359,6 +437,7 @@ void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) {
     SignalProxy::writeDataToDevice(socket, reply);
   }
 }
+*/
 
 void Core::setupClientSession(QTcpSocket *socket, UserId uid) {
   // Find or create session for validated user
@@ -367,6 +446,8 @@ void Core::setupClientSession(QTcpSocket *socket, UserId uid) {
   else sess = createSession(uid);
   // Hand over socket, session then sends state itself
   disconnect(socket, 0, this, 0);
+  blocksizes.remove(socket);
+  clientInfo.remove(socket);
   if(!sess) {
     qWarning() << qPrintable(tr("Could not initialize session for client %1!").arg(socket->peerAddress().toString()));
     socket->close();
@@ -384,14 +465,3 @@ SessionThread *Core::createSession(UserId uid, bool restore) {
   sess->start();
   return sess;
 }
-
-QStringList Core::availableStorageProviders() {
-  QStringList storageProviders;
-  if (SqliteStorage::isAvailable()) {
-    storageProviders.append(SqliteStorage::displayName());
-  }
-  // TODO: temporary
-  // storageProviders.append("MySQL");
-  
-  return storageProviders;
-}
index 091c0f7..6b0126c 100644 (file)
@@ -158,11 +158,13 @@ class Core : public QObject {
 
     SessionThread *createSession(UserId userId, bool restoreState = false);
     void setupClientSession(QTcpSocket *socket, UserId uid);
-    void processCoreSetup(QTcpSocket *socket, QVariantMap &msg);
+    void processClientMessage(QTcpSocket *socket, const QVariantMap &msg);
+    //void processCoreSetup(QTcpSocket *socket, QVariantMap &msg);
+    QString setupCore(const QVariant &setupData);
 
-    QStringList availableStorageProviders();
+    bool registerStorageBackend(Storage *);
+    void unregisterStorageBackend(Storage *);
 
-    UserId guiUser;
     QHash<UserId, SessionThread *> sessions;
     Storage *storage;
     QTimer _storageSyncTimer;
@@ -171,6 +173,8 @@ class Core : public QObject {
     QHash<QTcpSocket *, quint32> blocksizes;
     QHash<QTcpSocket *, QVariantMap> clientInfo;
 
+    QHash<QString, Storage *> _storageBackends;
+
     QDateTime startTime;
 
     bool configured;
index 28fb7d5..b7aae85 100644 (file)
@@ -28,12 +28,17 @@ CoreSettings::CoreSettings(const QString group) : Settings(group, Global::coreAp
 CoreSettings::~CoreSettings() {
 }
 
-void CoreSettings::setDatabaseSettings(const QVariant &data) {
-  setLocalValue("DatabaseSettings", data);
+void CoreSettings::setStorageSettings(const QVariant &data) {
+  setLocalValue("StorageSettings", data);
 }
 
-QVariant CoreSettings::databaseSettings(const QVariant &def) {
-  return localValue("DatabaseSettings", def);
+QVariant CoreSettings::storageSettings(const QVariant &def) {
+  return localValue("StorageSettings", def);
+}
+
+// FIXME remove
+QVariant CoreSettings::oldDbSettings() {
+  return localValue("DatabaseSettings");
 }
 
 void CoreSettings::setPort(const uint &port) {
index 0e204d1..749c8ec 100644 (file)
@@ -30,8 +30,10 @@ class CoreSettings : public Settings {
     virtual ~CoreSettings();
     CoreSettings(const QString group = "Core");
 
-    void setDatabaseSettings(const QVariant &data);
-    QVariant databaseSettings(const QVariant &def = QVariant());
+    void setStorageSettings(const QVariant &data);
+    QVariant storageSettings(const QVariant &def = QVariant());
+
+    QVariant oldDbSettings();  // FIXME remove
 
     void setPort(const uint &port);
     uint port(const uint &def = Global::defaultPort);
index 49476e9..900a193 100644 (file)
@@ -33,17 +33,19 @@ SqliteStorage::SqliteStorage(QObject *parent)
 SqliteStorage::~SqliteStorage() {
 }
 
-bool SqliteStorage::isAvailable() {
+bool SqliteStorage::isAvailable() const {
   if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
   return true;
 }
 
-QString SqliteStorage::displayName() {
+QString SqliteStorage::displayName() const {
   return QString("SQLite");
 }
 
-QString SqliteStorage::engineName() {
-  return SqliteStorage::displayName();
+QString SqliteStorage::description() const {
+  return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized "
+            "databases that do not require access via network. Use SQLite if your Quassel Core should store its data on the same machine "
+            "it is running on, and if you only expect a few users to use your core.");
 }
 
 int SqliteStorage::installedSchemaVersion() {
index adaabe7..b6ba748 100644 (file)
@@ -33,13 +33,14 @@ class SqliteStorage : public AbstractSqlStorage {
 public:
   SqliteStorage(QObject *parent = 0);
   virtual ~SqliteStorage();
-                         
+
 public slots:
   /* General */
   
-  static bool isAvailable();
-  static QString displayName();
-  virtual QString engineName() ;
+  bool isAvailable() const;
+  QString displayName() const;
+  QString description() const;
+
   // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc
   
   /* User handling */
index 6470b40..649bdc4 100644 (file)
@@ -42,32 +42,36 @@ class Storage : public QObject {
      *  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.
      */
-    static bool isAvailable() { return false; }
+    virtual bool isAvailable() const = 0;
 
     //! Returns the display name of the storage backend
-    /** \return A string that can be used by the GUI to describe the storage backend */
-    static QString displayName() { return ""; }
+    /** \return A string that can be used by the client to name the storage backend */
+    virtual QString displayName() const = 0;
+
+    //! Returns a description of this storage backend
+    /** \return A string that can be displayed by the client to describe the storage backend */
+    virtual QString description() const = 0;
 
     //! Setup the storage provider.
     /** This prepares the storage provider (e.g. create tables, etc.) for use within Quassel.
      *  \param settings   Hostname, port, username, password, ...
      *  \return True if and only if the storage provider was initialized successfully.
      */
-    virtual bool setup(const QVariantMap &settings = QVariantMap()) { Q_UNUSED(settings); return false; }
-    
+    virtual bool setup(const QVariantMap &settings = QVariantMap()) = 0;
+
     //! Initialize the storage provider
     /** \param settings   Hostname, port, username, password, ...  
      *  \return True if and only if the storage provider was initialized successfully.
      */
     virtual bool init(const QVariantMap &settings = QVariantMap()) = 0;
 
-  //! Makes temp data persistent
-  /** This Method is periodically called by the Quassel Core to make temporary
-   *  data persistant. This reduces the data loss drastically in the
-   *  unlikely case of a Core crash.
-   */
+    //! Makes temp data persistent
+    /** This Method is periodically called by the Quassel Core to make temporary
+    *  data persistant. This reduces the data loss drastically in the
+    *  unlikely case of a Core crash.
+    */
     virtual void sync() = 0;
-  
+
     // TODO: Add functions for configuring the backlog handling, i.e. defining auto-cleanup settings etc
 
     /* User handling */
diff --git a/src/qtui/coreconfigwizard.cpp b/src/qtui/coreconfigwizard.cpp
new file mode 100644 (file)
index 0000000..792fbc0
--- /dev/null
@@ -0,0 +1,259 @@
+/***************************************************************************
+ *   Copyright (C) 2005-08 by the Quassel IRC 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) version 3.                                           *
+ *                                                                         *
+ *   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 <QDebug>
+#include <QAbstractButton>
+
+#include "coreconfigwizard.h"
+
+//#include "client.h"
+//#include "identitiessettingspage.h"
+
+CoreConfigWizard::CoreConfigWizard(const QList<QVariant> &backends, QWidget *parent) : QWizard(parent) {
+  foreach(QVariant v, backends) _backends[v.toMap()["DisplayName"].toString()] = v;
+  setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this));
+  setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this));
+  setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this));
+  syncPage = new CoreConfigWizardPages::SyncPage(this);
+  connect(syncPage, SIGNAL(setupCore(const QString &)), this, SLOT(prepareCoreSetup(const QString &)));
+  setPage(SyncPage, syncPage);
+  syncRelayPage = new CoreConfigWizardPages::SyncRelayPage(this);
+  connect(syncRelayPage, SIGNAL(startOver()), this, SLOT(startOver()));
+  setPage(SyncRelayPage, syncRelayPage);
+  //setPage(Page_StorageDetails, new StorageDetailsPage());
+  //setPage(Page_Conclusion, new ConclusionPage(storageProviders));
+
+  setStartId(IntroPage);
+  //setStartId(StorageSelectionPage);
+
+#ifndef Q_WS_MAC
+  setWizardStyle(ModernStyle);
+#endif
+
+  setOption(HaveHelpButton, false);
+  setOption(NoBackButtonOnStartPage, true);
+  setOption(HaveNextButtonOnLastPage, false);
+  setOption(HaveFinishButtonOnEarlyPages, false);
+  setOption(NoCancelButton, true);
+  setOption(IndependentPages, true);
+  //setOption(ExtendedWatermarkPixmap, true);
+
+  setModal(true);
+
+  setWindowTitle(tr("Core Configuration Wizard"));
+  setPixmap(QWizard::LogoPixmap, QPixmap(":icons/quassel-icon.png"));
+}
+
+QHash<QString, QVariant> CoreConfigWizard::backends() const {
+  return _backends;
+}
+
+void CoreConfigWizard::prepareCoreSetup(const QString &backend) {
+  // Prevent the user from changing any settings he already specified...
+  foreach(int idx, visitedPages()) page(idx)->setEnabled(false);
+  QVariantMap foo;
+  foo["AdminUser"] = field("adminUser.user").toString();
+  foo["AdminPasswd"] = field("adminUser.password").toString();
+  foo["Backend"] = backend;
+  emit setupCore(foo);
+}
+
+void CoreConfigWizard::coreSetupSuccess() {
+  syncPage->setStatus(tr("Your core has been successfully configured. Logging you in..."));
+  syncPage->setError(false);
+  syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
+  QVariantMap loginData;
+  loginData["User"] = field("adminUser.user");
+  loginData["Password"] = field("adminUser.password");
+  loginData["RememberPasswd"] = field("adminUser.rememberPasswd");
+  emit loginToCore(loginData);
+}
+
+void CoreConfigWizard::coreSetupFailed(const QString &error) {
+  syncPage->setStatus(tr("Core configuration failed:<br><b>%1</b><br>Press <em>Next</em> to start over.").arg(error));
+  syncPage->setError(true);
+  syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
+  //foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
+  //setStartId(SyncPage);
+  //restart();
+
+}
+
+void CoreConfigWizard::startOver() {
+  foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
+  setStartId(CoreConfigWizard::AdminUserPage);
+  restart();
+}
+
+void CoreConfigWizard::loginSuccess() {
+  syncPage->setStatus(tr("Your are now logged into your freshly configured Quassel Core!<br>"
+                         "Please remember to configure your identities and networks now."));
+  syncPage->setComplete(true);
+  syncPage->setFinalPage(true);
+}
+
+void CoreConfigWizard::syncFinished() {
+  // TODO: display identities and networks settings if appropriate!
+  // accept();
+}
+
+namespace CoreConfigWizardPages {
+
+/*** Intro Page ***/
+
+IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent) {
+  ui.setupUi(this);
+  setTitle(tr("Introduction"));
+  //setSubTitle(tr("foobar"));
+  //setPixmap(QWizard::WatermarkPixmap, QPixmap(":icons/quassel-icon.png"));
+
+}
+
+int IntroPage::nextId() const {
+  return CoreConfigWizard::AdminUserPage;
+
+}
+
+/*** Admin User Page ***/
+
+AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent) {
+  ui.setupUi(this);
+  setTitle(tr("Create User Account"));
+  setSubTitle(tr("First, we will create a user account on the core. This first user will have administrator privileges."));
+
+  registerField("adminUser.user*", ui.user);
+  registerField("adminUser.password*", ui.password);
+  registerField("adminUser.password2*", ui.password2);
+  registerField("adminUser.rememberPasswd", ui.rememberPasswd);
+
+  //ui.user->setText("foo");
+  //ui.password->setText("foo");
+  //ui.password2->setText("foo");
+}
+
+int AdminUserPage::nextId() const {
+  return CoreConfigWizard::StorageSelectionPage;
+
+}
+
+bool AdminUserPage::isComplete() const {
+  bool ok = !ui.user->text().isEmpty() && !ui.password->text().isEmpty() && ui.password->text() == ui.password2->text();
+  return ok;
+}
+
+/*** Storage Selection Page ***/
+
+StorageSelectionPage::StorageSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent) : QWizardPage(parent) {
+  ui.setupUi(this);
+  _backends = backends;
+
+  setTitle(tr("Select Storage Backend"));
+  setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in."));
+  setCommitPage(true);
+
+  registerField("storage.backend", ui.backendList);
+
+  foreach(QString key, _backends.keys()) {
+    ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
+  }
+
+  on_backendList_currentIndexChanged();
+}
+
+int StorageSelectionPage::nextId() const {
+  return CoreConfigWizard::SyncPage;
+}
+
+QString StorageSelectionPage::selectedBackend() const {
+  return ui.backendList->currentText();
+}
+
+void StorageSelectionPage::on_backendList_currentIndexChanged() {
+  QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
+  ui.description->setText(_backends[backend].toMap()["Description"].toString());
+}
+
+/*** Sync Page ***/
+
+SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent) {
+  ui.setupUi(this);
+  setTitle(tr("Storing Your Settings"));
+  setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically."));
+}
+
+void SyncPage::initializePage() {
+  complete = false;
+  hasError = false;
+  QString backend = qobject_cast<StorageSelectionPage *>(wizard()->page(CoreConfigWizard::StorageSelectionPage))->selectedBackend();
+  Q_ASSERT(!backend.isEmpty());
+  ui.user->setText(wizard()->field("adminUser.user").toString());
+  ui.backend->setText(backend);
+  emit setupCore(backend);
+}
+
+int SyncPage::nextId() const {
+  if(!hasError) return -1;
+  return CoreConfigWizard::SyncRelayPage;
+}
+
+bool SyncPage::isComplete() const {
+  return complete;
+}
+
+void SyncPage::setStatus(const QString &status) {
+  ui.status->setText(status);
+}
+
+void SyncPage::setError(bool e) {
+  hasError = e;
+}
+
+void SyncPage::setComplete(bool c) {
+  complete = c;
+  completeChanged();
+}
+
+/*** Sync Relay Page ***/
+
+SyncRelayPage::SyncRelayPage(QWidget *parent) : QWizardPage(parent) {
+  mode = Success;
+}
+
+void SyncRelayPage::setMode(Mode m) {
+  mode = m;
+}
+
+/*
+void SyncRelayPage::initializePage() {
+  return;
+  if(mode == Success) {
+    wizard()->accept();
+  } else {
+    emit startOver();
+  }
+}
+*/
+
+int SyncRelayPage::nextId() const {
+  emit startOver();
+  return 0;
+}
+
+};  /* namespace CoreConfigWizardPages */
diff --git a/src/qtui/coreconfigwizard.h b/src/qtui/coreconfigwizard.h
new file mode 100644 (file)
index 0000000..1940fee
--- /dev/null
@@ -0,0 +1,155 @@
+/***************************************************************************
+ *   Copyright (C) 2005-08 by the Quassel IRC 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) version 3.                                           *
+ *                                                                         *
+ *   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 _CORECONFIGWIZARD_H_
+#define _CORECONFIGWIZARD_H_
+
+#include <QHash>
+#include <QWizard>
+#include <QVariantMap>
+
+#include "ui_coreconfigwizardintropage.h"
+#include "ui_coreconfigwizardadminuserpage.h"
+#include "ui_coreconfigwizardstorageselectionpage.h"
+#include "ui_coreconfigwizardsyncpage.h"
+
+namespace CoreConfigWizardPages {
+  class SyncPage;
+  class SyncRelayPage;
+};
+
+class CoreConfigWizard : public QWizard {
+  Q_OBJECT
+
+  public:
+    enum {
+      IntroPage,
+      AdminUserPage,
+      StorageSelectionPage,
+      SyncPage,
+      SyncRelayPage,
+      StorageDetailsPage,
+      ConclusionPage
+    };
+
+    CoreConfigWizard(const QList<QVariant> &backends, QWidget *parent = 0);
+    QHash<QString, QVariant> backends() const;
+
+  signals:
+    void setupCore(const QVariant &setupData);
+    void loginToCore(const QVariantMap &loginData);
+
+  public slots:
+    void loginSuccess();
+    void syncFinished();
+
+  private slots:
+    void prepareCoreSetup(const QString &backend);
+    void coreSetupSuccess();
+    void coreSetupFailed(const QString &);
+    void startOver();
+
+  private:
+    QHash<QString, QVariant> _backends;
+    CoreConfigWizardPages::SyncPage *syncPage;
+    CoreConfigWizardPages::SyncRelayPage *syncRelayPage;
+};
+
+namespace CoreConfigWizardPages {
+
+  class IntroPage : public QWizardPage {
+    Q_OBJECT
+
+    public:
+      IntroPage(QWidget *parent = 0);
+      int nextId() const;
+    private:
+      Ui::CoreConfigWizardIntroPage ui;
+  };
+
+  class AdminUserPage : public QWizardPage {
+    Q_OBJECT
+
+    public:
+      AdminUserPage(QWidget *parent = 0);
+      int nextId() const;
+      bool isComplete() const;
+    private:
+      Ui::CoreConfigWizardAdminUserPage ui;
+  };
+
+  class StorageSelectionPage : public QWizardPage {
+    Q_OBJECT
+
+    public:
+      StorageSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent = 0);
+      int nextId() const;
+      QString selectedBackend() const;
+    private slots:
+      void on_backendList_currentIndexChanged();
+    private:
+      Ui::CoreConfigWizardStorageSelectionPage ui;
+      QHash<QString, QVariant> _backends;
+  };
+
+  class SyncPage : public QWizardPage {
+    Q_OBJECT
+
+    public:
+      SyncPage(QWidget *parent = 0);
+      void initializePage();
+      int nextId() const;
+      bool isComplete() const;
+
+    public slots:
+      void setStatus(const QString &status);
+      void setError(bool);
+      void setComplete(bool);
+
+    signals:
+      void setupCore(const QString &backend);
+
+    private:
+      Ui::CoreConfigWizardSyncPage ui;
+      bool complete;
+      bool hasError;
+  };
+
+  class SyncRelayPage : public QWizardPage {
+    Q_OBJECT
+
+    public:
+      SyncRelayPage(QWidget *parent = 0);
+      int nextId() const;
+      enum Mode { Success, Error };
+
+    public slots:
+      void setMode(Mode);
+
+    signals:
+      void startOver() const;
+
+    private:
+      Mode mode;
+  };
+
+}
+
+#endif
index 250c008..a8dc482 100644 (file)
 
 #include "clientsettings.h"
 #include "clientsyncer.h"
+#include "coreconfigwizard.h"
 
 CoreConnectDlg::CoreConnectDlg(QWidget *parent, bool autoconnect) : QDialog(parent) {
   ui.setupUi(this);
 
   clientSyncer = new ClientSyncer(this);
+  wizard = 0;
 
   setAttribute(Qt::WA_DeleteOnClose);
 
@@ -62,11 +64,12 @@ CoreConnectDlg::CoreConnectDlg(QWidget *parent, bool autoconnect) : QDialog(pare
   connect(clientSyncer, SIGNAL(startLogin()), this, SLOT(startLogin()));
   connect(clientSyncer, SIGNAL(loginFailed(const QString &)), this, SLOT(loginFailed(const QString &)));
   connect(clientSyncer, SIGNAL(loginSuccess()), this, SLOT(startSync()));
+  connect(clientSyncer, SIGNAL(startCoreSetup(const QVariantList &)), this, SLOT(startCoreConfig(const QVariantList &)));
   connect(clientSyncer, SIGNAL(sessionProgress(quint32, quint32)), this, SLOT(coreSessionProgress(quint32, quint32)));
   connect(clientSyncer, SIGNAL(networksProgress(quint32, quint32)), this, SLOT(coreNetworksProgress(quint32, quint32)));
   connect(clientSyncer, SIGNAL(channelsProgress(quint32, quint32)), this, SLOT(coreChannelsProgress(quint32, quint32)));
   connect(clientSyncer, SIGNAL(ircUsersProgress(quint32, quint32)), this, SLOT(coreIrcUsersProgress(quint32, quint32)));
-  connect(clientSyncer, SIGNAL(syncFinished()), this, SLOT(accept()));
+  connect(clientSyncer, SIGNAL(syncFinished()), this, SLOT(syncFinished()));
 
   connect(ui.user, SIGNAL(textChanged(const QString &)), this, SLOT(setLoginWidgetStates()));
   connect(ui.password, SIGNAL(textChanged(const QString &)), this, SLOT(setLoginWidgetStates()));
@@ -283,18 +286,33 @@ void CoreConnectDlg::startLogin() {
 }
 
 void CoreConnectDlg::doLogin() {
+  QVariantMap loginData;
+  loginData["User"] = ui.user->text();
+  loginData["Password"] = ui.password->text();
+  loginData["RememberPasswd"] = ui.rememberPasswd->isChecked();
+  doLogin(loginData);
+}
+
+void CoreConnectDlg::doLogin(const QVariantMap &loginData) {
+  disconnect(ui.loginButtonBox, 0, this, 0);
+  connect(ui.loginButtonBox, SIGNAL(accepted()), this, SLOT(doLogin()));
+  connect(ui.loginButtonBox, SIGNAL(rejected()), this, SLOT(restartPhaseNull()));
+  ui.loginStack->setCurrentWidget(ui.loginCredentialsPage);
   ui.loginGroup->setTitle(tr("Logging in..."));
   ui.user->setDisabled(true);
   ui.password->setDisabled(true);
   ui.rememberPasswd->setDisabled(true);
   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
-  accountData["User"] = ui.user->text();
-  accountData["RememberPasswd"] = ui.rememberPasswd->isChecked();
-  if(ui.rememberPasswd->isChecked()) accountData["Password"] = ui.password->text();
+  accountData["User"] = loginData["User"];
+  accountData["RememberPasswd"] = loginData["RememberPasswd"];
+  if(loginData["RememberPasswd"].toBool()) accountData["Password"] = loginData["Password"];
   else accountData.remove("Password");
+  ui.user->setText(loginData["User"].toString());
+  ui.password->setText(loginData["Password"].toString());
+  ui.rememberPasswd->setChecked(loginData["RememberPasswd"].toBool());
   CoreAccountSettings s;
   s.storeAccountData(account, accountData);
-  clientSyncer->loginToCore(ui.user->text(), ui.password->text());
+  clientSyncer->loginToCore(loginData["User"].toString(), loginData["Password"].toString());
 }
 
 void CoreConnectDlg::setLoginWidgetStates() {
@@ -302,6 +320,10 @@ void CoreConnectDlg::setLoginWidgetStates() {
 }
 
 void CoreConnectDlg::loginFailed(const QString &error) {
+  if(wizard) {
+    wizard->reject();
+  }
+  ui.loginStack->setCurrentWidget(ui.loginCredentialsPage);
   ui.loginGroup->setTitle(tr("Login"));
   ui.user->setEnabled(true);
   ui.password->setEnabled(true);
@@ -312,6 +334,42 @@ void CoreConnectDlg::loginFailed(const QString &error) {
   doingAutoConnect = false;
 }
 
+void CoreConnectDlg::startCoreConfig(const QVariantList &backends) {
+  storageBackends = backends;
+  ui.loginStack->setCurrentWidget(ui.coreConfigPage);
+
+  //on_launchCoreConfigWizard_clicked();
+
+}
+
+void CoreConnectDlg::on_launchCoreConfigWizard_clicked() {
+  Q_ASSERT(!wizard);
+  wizard = new CoreConfigWizard(storageBackends, this);
+  connect(wizard, SIGNAL(setupCore(const QVariant &)), clientSyncer, SLOT(doCoreSetup(const QVariant &)));
+  connect(wizard, SIGNAL(loginToCore(const QVariantMap &)), this, SLOT(doLogin(const QVariantMap &)));
+  connect(clientSyncer, SIGNAL(coreSetupSuccess()), wizard, SLOT(coreSetupSuccess()));
+  connect(clientSyncer, SIGNAL(coreSetupFailed(const QString &)), wizard, SLOT(coreSetupFailed(const QString &)));
+  connect(wizard, SIGNAL(accepted()), this, SLOT(configWizardAccepted()));
+  connect(wizard, SIGNAL(rejected()), this, SLOT(configWizardRejected()));
+  connect(clientSyncer, SIGNAL(loginSuccess()), wizard, SLOT(loginSuccess()));
+  connect(clientSyncer, SIGNAL(syncFinished()), wizard, SLOT(syncFinished()));
+  wizard->show();
+}
+
+void CoreConnectDlg::configWizardAccepted() {
+
+  wizard->deleteLater();
+  wizard = 0;
+}
+
+void CoreConnectDlg::configWizardRejected() {
+
+  wizard->deleteLater();
+  wizard = 0;
+  //exit(1); // FIXME
+}
+
+
 /************************************************************
  * Phase Three: Syncing
  ************************************************************/
@@ -335,7 +393,6 @@ void CoreConnectDlg::startSync() {
   ui.loginButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
 }
 
-
 void CoreConnectDlg::coreSessionProgress(quint32 val, quint32 max) {
   ui.sessionProgress->setRange(0, max);
   ui.sessionProgress->setValue(val);
@@ -379,6 +436,15 @@ void CoreConnectDlg::coreIrcUsersProgress(quint32 val, quint32 max) {
   }
 }
 
+void CoreConnectDlg::syncFinished() {
+  if(!wizard) accept();
+  else {
+    hide();
+    disconnect(wizard, 0, this, 0);
+    connect(wizard, SIGNAL(finished(int)), this, SLOT(accept()));
+  }
+}
+
 /*****************************************************************************************
  * CoreAccountEditDlg
  *****************************************************************************************/
index 69e3839..42638e5 100644 (file)
@@ -29,6 +29,7 @@
 #include "ui_coreaccounteditdlg.h"
 
 class ClientSyncer;
+class CoreConfigWizard;
 
 class CoreConnectDlg : public QDialog {
   Q_OBJECT
@@ -64,12 +65,18 @@ class CoreConnectDlg : public QDialog {
     /*** Phase Two: Login ***/
     void startLogin();
     void doLogin();
+    void doLogin(const QVariantMap &loginData);
     void loginFailed(const QString &);
+    void startCoreConfig(const QVariantList &backends);
+    void configWizardAccepted();
+    void configWizardRejected();
+    void on_launchCoreConfigWizard_clicked();
 
     void setLoginWidgetStates();
 
     /*** Phase Three: Sync ***/
     void startSync();
+    void syncFinished();
 
     void coreSessionProgress(quint32, quint32);
     void coreNetworksProgress(quint32, quint32);
@@ -86,7 +93,10 @@ class CoreConnectDlg : public QDialog {
 
     bool doingAutoConnect;
 
+    QVariantList storageBackends;
+
     ClientSyncer *clientSyncer;
+    CoreConfigWizard *wizard;
 };
 
 class CoreAccountEditDlg : public QDialog {
index 485ac67..86a7693 100644 (file)
@@ -28,7 +28,7 @@
 #include "buffermodel.h"
 #include "bufferinfo.h"
 #include "identity.h"
-#include "network.h";
+#include "network.h"
 
 class InputWidget : public QWidget {
   Q_OBJECT
index d1d6da2..29c0e02 100644 (file)
@@ -1,16 +1,17 @@
 DEPMOD = uisupport common client
 QT_MOD = core gui network
 
-SRCS += bufferwidget.cpp chatline-old.cpp chatwidget.cpp coreconnectdlg.cpp configwizard.cpp debugconsole.cpp inputwidget.cpp \
+SRCS += bufferwidget.cpp chatline-old.cpp chatwidget.cpp coreconfigwizard.cpp coreconnectdlg.cpp configwizard.cpp debugconsole.cpp inputwidget.cpp \
         mainwin.cpp nicklistwidget.cpp qtui.cpp qtuistyle.cpp settingsdlg.cpp settingspagedlg.cpp \
         topicwidget.cpp verticaldock.cpp
 
-HDRS += bufferwidget.h chatline-old.h chatwidget.h configwizard.h debugconsole.h inputwidget.h \
+HDRS += bufferwidget.h chatline-old.h chatwidget.h coreconfigwizard.h configwizard.h debugconsole.h inputwidget.h \
         coreconnectdlg.h mainwin.h nicklistwidget.h qtui.h qtuistyle.h settingsdlg.h settingspagedlg.h \
         topicwidget.h verticaldock.h
 
 FORMNAMES = mainwin.ui coreaccounteditdlg.ui coreconnectdlg.ui bufferviewwidget.ui bufferwidget.ui nicklistwidget.ui settingsdlg.ui \
-            settingspagedlg.ui topicwidget.ui debugconsole.ui inputwidget.ui
+            settingspagedlg.ui topicwidget.ui debugconsole.ui inputwidget.ui \
+            coreconfigwizardintropage.ui coreconfigwizardadminuserpage.ui coreconfigwizardstorageselectionpage.ui coreconfigwizardsyncpage.ui
 
 for(ui, FORMNAMES) {
   FRMS += ui/$${ui}
index 035993b..b02e711 100644 (file)
@@ -113,7 +113,6 @@ void SettingsDlg::itemSelected() {
 
 void SettingsDlg::setButtonStates() {
   SettingsPage *sp = currentPage();
-  ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(sp && sp->hasChanged());
   ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(sp && sp->hasChanged());
   ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(sp && sp->hasChanged());
   ui.buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(sp && sp->hasDefaults());
diff --git a/src/qtui/ui/coreconfigwizardadminuserpage.ui b/src/qtui/ui/coreconfigwizardadminuserpage.ui
new file mode 100644 (file)
index 0000000..fcab077
--- /dev/null
@@ -0,0 +1,95 @@
+<ui version="4.0" >
+ <class>CoreConfigWizardAdminUserPage</class>
+ <widget class="QWidget" name="CoreConfigWizardAdminUserPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>415</width>
+    <height>273</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <item>
+    <layout class="QGridLayout" >
+     <item row="0" column="0" >
+      <widget class="QLabel" name="label_2" >
+       <property name="text" >
+        <string>Username:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" >
+      <widget class="QLineEdit" name="user" />
+     </item>
+     <item row="1" column="0" >
+      <widget class="QLabel" name="label_3" >
+       <property name="text" >
+        <string>Password:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" >
+      <widget class="QLineEdit" name="password" >
+       <property name="echoMode" >
+        <enum>QLineEdit::Password</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0" >
+      <widget class="QLabel" name="label_4" >
+       <property name="text" >
+        <string>Repeat password:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1" >
+      <widget class="QLineEdit" name="password2" >
+       <property name="echoMode" >
+        <enum>QLineEdit::Password</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1" >
+      <widget class="QCheckBox" name="rememberPasswd" >
+       <property name="text" >
+        <string>Remember password</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label" >
+     <property name="text" >
+      <string>&lt;em>Note: Adding more users and changing your username/password has not been implemented yet.</string>
+     </property>
+     <property name="alignment" >
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <property name="wordWrap" >
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>405</width>
+       <height>51</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qtui/ui/coreconfigwizardintropage.ui b/src/qtui/ui/coreconfigwizardintropage.ui
new file mode 100644 (file)
index 0000000..2c14ad0
--- /dev/null
@@ -0,0 +1,30 @@
+<ui version="4.0" >
+ <class>CoreConfigWizardIntroPage</class>
+ <widget class="QWidget" name="CoreConfigWizardIntroPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>430</width>
+    <height>202</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" >
+   <item>
+    <widget class="QLabel" name="label" >
+     <property name="text" >
+      <string>This wizard will guide you through the setup of your Quassel Core.</string>
+     </property>
+     <property name="alignment" >
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qtui/ui/coreconfigwizardstorageselectionpage.ui b/src/qtui/ui/coreconfigwizardstorageselectionpage.ui
new file mode 100644 (file)
index 0000000..261426b
--- /dev/null
@@ -0,0 +1,92 @@
+<ui version="4.0" >
+ <class>CoreConfigWizardStorageSelectionPage</class>
+ <widget class="QWidget" name="CoreConfigWizardStorageSelectionPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>161</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <item>
+    <layout class="QHBoxLayout" >
+     <item>
+      <widget class="QLabel" name="label" >
+       <property name="text" >
+        <string>Storage Backend:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="backendList" >
+       <property name="sizePolicy" >
+        <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="insertPolicy" >
+        <enum>QComboBox::InsertAtBottom</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer>
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" >
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="descriptionBox" >
+     <property name="title" >
+      <string>Description</string>
+     </property>
+     <layout class="QVBoxLayout" >
+      <item>
+       <widget class="QLabel" name="description" >
+        <property name="text" >
+         <string>Foobar</string>
+        </property>
+        <property name="alignment" >
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+        </property>
+        <property name="wordWrap" >
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>392</width>
+       <height>51</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qtui/ui/coreconfigwizardsyncpage.ui b/src/qtui/ui/coreconfigwizardsyncpage.ui
new file mode 100644 (file)
index 0000000..610c17a
--- /dev/null
@@ -0,0 +1,116 @@
+<ui version="4.0" >
+ <class>CoreConfigWizardSyncPage</class>
+ <widget class="QWidget" name="CoreConfigWizardSyncPage" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <item>
+    <widget class="QGroupBox" name="groupBox" >
+     <property name="title" >
+      <string>Your Choices</string>
+     </property>
+     <layout class="QVBoxLayout" >
+      <item>
+       <layout class="QHBoxLayout" >
+        <item>
+         <layout class="QGridLayout" >
+          <item row="0" column="0" >
+           <widget class="QLabel" name="label" >
+            <property name="font" >
+             <font>
+              <weight>75</weight>
+              <bold>true</bold>
+             </font>
+            </property>
+            <property name="text" >
+             <string>Admin User:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1" >
+           <widget class="QLabel" name="user" >
+            <property name="text" >
+             <string>foo</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0" >
+           <widget class="QLabel" name="label_2" >
+            <property name="font" >
+             <font>
+              <weight>75</weight>
+              <bold>true</bold>
+             </font>
+            </property>
+            <property name="text" >
+             <string>Storage Backend:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1" >
+           <widget class="QLabel" name="backend" >
+            <property name="text" >
+             <string>bar</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QLabel" name="status" >
+     <property name="text" >
+      <string>Please wait while your settings are being transmitted to the core...</string>
+     </property>
+     <property name="alignment" >
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <property name="wordWrap" >
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
index 91ce6ca..f53ea50 100644 (file)
@@ -5,8 +5,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>511</width>
-    <height>389</height>
+    <width>493</width>
+    <height>331</height>
    </rect>
   </property>
   <property name="windowTitle" >
@@ -21,7 +21,7 @@
      <item>
       <widget class="QStackedWidget" name="stackedWidget" >
        <property name="currentIndex" >
-        <number>2</number>
+        <number>1</number>
        </property>
        <widget class="QWidget" name="accountPage" >
         <layout class="QVBoxLayout" >
@@ -46,7 +46,7 @@
                  <string>Edit...</string>
                 </property>
                 <property name="icon" >
-                 <iconset resource="../../icons/icons.qrc" >:/16x16/actions/oxygen/16x16/actions/configure.png</iconset>
+                 <iconset/>
                 </property>
                </widget>
               </item>
                   </item>
                  </layout>
                 </widget>
+                <widget class="QWidget" name="coreConfigPage" >
+                 <layout class="QVBoxLayout" >
+                  <item>
+                   <spacer>
+                    <property name="orientation" >
+                     <enum>Qt::Vertical</enum>
+                    </property>
+                    <property name="sizeHint" >
+                     <size>
+                      <width>20</width>
+                      <height>71</height>
+                     </size>
+                    </property>
+                   </spacer>
+                  </item>
+                  <item>
+                   <widget class="QGroupBox" name="groupBox_4" >
+                    <property name="title" >
+                     <string>Configure your Quassel Core</string>
+                    </property>
+                    <layout class="QVBoxLayout" >
+                     <item>
+                      <widget class="QLabel" name="label_7" >
+                       <property name="text" >
+                        <string>The Quassel Core you are connected to is not configured yet. You may now launch a configuration wizard that helps you setting up your Core.</string>
+                       </property>
+                       <property name="alignment" >
+                        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+                       </property>
+                       <property name="wordWrap" >
+                        <bool>true</bool>
+                       </property>
+                      </widget>
+                     </item>
+                     <item>
+                      <spacer>
+                       <property name="orientation" >
+                        <enum>Qt::Vertical</enum>
+                       </property>
+                       <property name="sizeHint" >
+                        <size>
+                         <width>20</width>
+                         <height>40</height>
+                        </size>
+                       </property>
+                      </spacer>
+                     </item>
+                     <item>
+                      <layout class="QHBoxLayout" >
+                       <item>
+                        <spacer>
+                         <property name="orientation" >
+                          <enum>Qt::Horizontal</enum>
+                         </property>
+                         <property name="sizeHint" >
+                          <size>
+                           <width>40</width>
+                           <height>20</height>
+                          </size>
+                         </property>
+                        </spacer>
+                       </item>
+                       <item>
+                        <widget class="QPushButton" name="launchCoreConfigWizard" >
+                         <property name="text" >
+                          <string>Launch Wizard</string>
+                         </property>
+                        </widget>
+                       </item>
+                       <item>
+                        <spacer>
+                         <property name="orientation" >
+                          <enum>Qt::Horizontal</enum>
+                         </property>
+                         <property name="sizeHint" >
+                          <size>
+                           <width>40</width>
+                           <height>20</height>
+                          </size>
+                         </property>
+                        </spacer>
+                       </item>
+                      </layout>
+                     </item>
+                    </layout>
+                   </widget>
+                  </item>
+                 </layout>
+                </widget>
                 <widget class="QWidget" name="loginEmptyPage" />
                </widget>
               </item>
index 2ce7ccb..b4d653b 100644 (file)
@@ -26,7 +26,7 @@
 class UiSettings : public ClientSettings {
 
   public:
-    UiSettings(const QString &group = "UI");
+    UiSettings(const QString &group = "Ui");
 
     void setValue(const QString &key, const QVariant &data);
     QVariant value(const QString &key, const QVariant &def = QVariant());
index bf713bf..741f6a5 100644 (file)
@@ -4,15 +4,15 @@
 { using namespace Global;
 
   quasselVersion = "0.2.0-pre";
-  quasselDate = "2008-02-06";
-  quasselBuild = 478;
+  quasselDate = "2008-02-08";
+  quasselBuild = 480;
 
   //! Minimum client build number the core needs
-  clientBuildNeeded = 464;
+  clientBuildNeeded = 480;
   clientVersionNeeded = quasselVersion;
 
   //! Minimum core build number the client needs
-  coreBuildNeeded = 464;
+  coreBuildNeeded = 480;
   coreVersionNeeded = quasselVersion;
 
 }