1 /***************************************************************************
2 * Copyright (C) 2005-07 by the Quassel IRC Team *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
22 #include "coresession.h"
23 #include "coresettings.h"
24 #include "sqlitestorage.h"
27 #include <QMetaObject>
28 #include <QMetaMethod>
30 Core *Core::instanceptr = 0;
32 Core *Core::instance() {
33 if(instanceptr) return instanceptr;
34 instanceptr = new Core();
39 void Core::destroy() {
52 QVariantMap dbSettings = s.databaseSettings().toMap();
53 QString hname = dbSettings["Type"].toString().toLower();
54 hname[0] = hname[0].toUpper();
55 hname = "initStorage" + hname;
56 if (!QMetaObject::invokeMethod(this, hname.toAscii(), Q_RETURN_ARG(bool, configured), Q_ARG(QVariantMap, dbSettings), Q_ARG(bool, false))) {
57 qWarning("No database backend configured.");
61 qWarning("Core is currently not configured!");
64 connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
65 startListening(s.port());
69 bool Core::initStorageSqlite(QVariantMap dbSettings, bool setup) {
70 if (!SqliteStorage::isAvailable()) {
71 qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
74 qDebug() << "Deleting old storage object.";
79 storage = new SqliteStorage();
80 if (setup && !storage->setup(dbSettings)) {
84 return storage->init(dbSettings);
95 CoreSession *Core::session(UserId uid) {
96 Core *core = instance();
97 if(core->sessions.contains(uid)) return core->sessions[uid];
101 CoreSession *Core::localSession() {
102 Core *core = instance();
103 if(core->guiUser && core->sessions.contains(core->guiUser)) return core->sessions[core->guiUser];
107 CoreSession *Core::createSession(UserId uid) {
108 Core *core = instance();
109 Q_ASSERT(!core->sessions.contains(uid));
110 CoreSession *sess = new CoreSession(uid, core->storage);
111 core->sessions[uid] = sess;
115 bool Core::startListening(uint port) {
116 if(!server.listen(QHostAddress::Any, port)) {
117 qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii());
120 qDebug() << "Listening for GUI clients on port" << server.serverPort();
124 void Core::stopListening() {
126 qDebug() << "No longer listening for GUI clients.";
129 void Core::incomingConnection() {
130 // TODO implement SSL
131 while (server.hasPendingConnections()) {
132 QTcpSocket *socket = server.nextPendingConnection();
133 connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
134 connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
135 blockSizes.insert(socket, (quint32)0);
136 qDebug() << "Client connected from " << socket->peerAddress().toString();
140 qDebug() << "Closing server for basic setup.";
145 void Core::clientHasData() {
146 QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
147 Q_ASSERT(socket && blockSizes.contains(socket));
148 quint32 bsize = blockSizes.value(socket);
150 if(readDataFromDevice(socket, bsize, item)) {
151 // we need to auth the client
153 QVariantMap msg = item.toMap();
154 if (msg["GuiProtocol"].toUInt() != GUI_PROTOCOL) {
155 throw Exception("GUI client version mismatch");
158 processClientInit(socket, msg);
160 processCoreSetup(socket, msg);
162 } catch(Storage::AuthError) {
163 qWarning() << "Authentification error!"; // FIXME: send auth error to client
166 } catch(Exception e) {
167 qWarning() << "Client init error:" << e.msg();
172 blockSizes[socket] = bsize = 0; // FIXME blockSizes aufr�um0rn!
175 // FIXME: no longer called, since connection handling is now in SignalProxy
176 // No, it is called as long as core is not configured. (kaffeedoktor)
177 void Core::clientDisconnected() {
178 QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
179 blockSizes.remove(socket);
180 qDebug() << "Client disconnected.";
182 // make server listen again if still not configured
187 // TODO remove unneeded sessions - if necessary/possible...
190 QVariant Core::connectLocalClient(QString user, QString passwd) {
191 UserId uid = instance()->storage->validateUser(user, passwd);
192 QVariant reply = instance()->initSession(uid);
193 instance()->guiUser = uid;
194 qDebug() << "Local client connected.";
198 void Core::disconnectLocalClient() {
199 qDebug() << "Local client disconnected.";
200 instance()->guiUser = 0;
203 void Core::processClientInit(QTcpSocket *socket, const QVariantMap &msg) {
206 UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString()); // throws exception if this failed
207 reply["StartWizard"] = false;
208 reply["Reply"] = initSession(uid);
209 disconnect(socket, 0, this, 0);
210 sessions[uid]->addClient(socket);
211 qDebug() << "Client initialized successfully.";
212 writeDataToDevice(socket, reply);
215 void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) {
216 if(msg["HasSettings"].toBool()) {
218 auth["User"] = msg["User"];
219 auth["Password"] = msg["Password"];
221 msg.remove("Password");
222 qDebug() << "Initializing storage provider" << msg["Type"].toString();
223 QString hname = msg["Type"].toString().toLower();
224 hname[0] = hname[0].toUpper();
225 hname = "initStorage" + hname;
226 if (!QMetaObject::invokeMethod(this, hname.toAscii(), Q_RETURN_ARG(bool, configured), Q_ARG(QVariantMap, msg), Q_ARG(bool, true))) {
227 qWarning("No database backend configured.");
230 // notify client to start wizard again
231 qWarning("Core is currently not configured!");
233 reply["StartWizard"] = true;
234 reply["StorageProviders"] = availableStorageProviders();
235 writeDataToDevice(socket, reply);
237 // write coresettings
239 s.setDatabaseSettings(msg);
240 // write admin user to database & make the core listen again to connections
241 storage->addUser(auth["User"].toString(), auth["Password"].toString());
243 // continue the normal procedure
244 processClientInit(socket, auth);
247 // notify client to start wizard
249 reply["StartWizard"] = true;
250 reply["StorageProviders"] = availableStorageProviders();
251 writeDataToDevice(socket, reply);
255 QVariant Core::initSession(UserId uid) {
256 // Find or create session for validated user
258 if(sessions.contains(uid))
259 sess = sessions[uid];
261 sess = createSession(uid);
263 reply["SessionState"] = sess->sessionState();
267 QStringList Core::availableStorageProviders() {
268 QStringList storageProviders;
269 if (SqliteStorage::isAvailable()) {
270 storageProviders.append(SqliteStorage::displayName());
273 storageProviders.append("MySQL");
275 return storageProviders;