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 "signalproxy.h"
25 #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());
70 bool Core::initStorageSqlite(QVariantMap dbSettings, bool setup) {
71 if (!SqliteStorage::isAvailable()) {
72 qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
75 qDebug() << "Deleting old storage object.";
80 storage = new SqliteStorage();
81 if (setup && !storage->setup(dbSettings)) {
85 return storage->init(dbSettings);
96 void Core::restoreState() {
97 Q_ASSERT(!instance()->sessions.count());
99 QList<QVariant> users = s.coreState().toList();
100 if(users.count() > 0) {
101 qDebug() << "Restoring previous core state...";
102 foreach(QVariant v, users) {
103 QVariantMap m = v.toMap();
104 if(m.contains("UserId")) {
105 CoreSession *sess = createSession(m["UserId"].toUInt());
106 sess->restoreState(m["State"]);
112 void Core::saveState() {
114 QList<QVariant> users;
115 foreach(CoreSession *sess, instance()->sessions.values()) {
117 m["UserId"] = sess->userId();
118 m["State"] = sess->state();
121 s.setCoreState(users);
124 CoreSession *Core::session(UserId uid) {
125 Core *core = instance();
126 if(core->sessions.contains(uid)) return core->sessions[uid];
130 CoreSession *Core::localSession() {
131 Core *core = instance();
132 if(core->guiUser && core->sessions.contains(core->guiUser)) return core->sessions[core->guiUser];
136 CoreSession *Core::createSession(UserId uid) {
137 Core *core = instance();
138 Q_ASSERT(!core->sessions.contains(uid));
139 CoreSession *sess = new CoreSession(uid, core->storage);
140 core->sessions[uid] = sess;
144 bool Core::startListening(uint port) {
145 if(!server.listen(QHostAddress::Any, port)) {
146 qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii());
149 qDebug() << "Listening for GUI clients on port" << server.serverPort();
153 void Core::stopListening() {
155 qDebug() << "No longer listening for GUI clients.";
158 void Core::incomingConnection() {
159 // TODO implement SSL
160 while (server.hasPendingConnections()) {
161 QTcpSocket *socket = server.nextPendingConnection();
162 connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
163 connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
164 blockSizes.insert(socket, (quint32)0);
165 qDebug() << "Client connected from " << socket->peerAddress().toString();
169 qDebug() << "Closing server for basic setup.";
174 void Core::clientHasData() {
175 QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
176 Q_ASSERT(socket && blockSizes.contains(socket));
177 quint32 bsize = blockSizes.value(socket);
179 if(SignalProxy::readDataFromDevice(socket, bsize, item)) {
180 // we need to auth the client
182 QVariantMap msg = item.toMap();
183 if (msg["GuiProtocol"].toUInt() != GUI_PROTOCOL) {
184 throw Exception("GUI client version mismatch");
187 processClientInit(socket, msg);
189 processCoreSetup(socket, msg);
191 } catch(Storage::AuthError) {
192 qWarning() << "Authentification error!"; // FIXME: send auth error to client
195 } catch(Exception e) {
196 qWarning() << "Client init error:" << e.msg();
201 blockSizes[socket] = bsize = 0; // FIXME blockSizes aufr�um0rn!
204 // FIXME: no longer called, since connection handling is now in SignalProxy
205 // No, it is called as long as core is not configured. (kaffeedoktor)
206 void Core::clientDisconnected() {
207 QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
208 blockSizes.remove(socket);
209 qDebug() << "Client disconnected.";
211 // make server listen again if still not configured
216 // TODO remove unneeded sessions - if necessary/possible...
219 QVariant Core::connectLocalClient(QString user, QString passwd) {
220 UserId uid = instance()->storage->validateUser(user, passwd);
221 QVariant reply = instance()->initSession(uid);
222 instance()->guiUser = uid;
223 qDebug() << "Local client connected.";
227 void Core::disconnectLocalClient() {
228 qDebug() << "Local client disconnected.";
229 instance()->guiUser = 0;
232 void Core::processClientInit(QTcpSocket *socket, const QVariantMap &msg) {
235 UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString()); // throws exception if this failed
236 reply["StartWizard"] = false;
237 reply["Reply"] = initSession(uid);
238 disconnect(socket, 0, this, 0);
239 sessions[uid]->addClient(socket);
240 qDebug() << "Client initialized successfully.";
241 SignalProxy::writeDataToDevice(socket, reply);
244 void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) {
245 if(msg["HasSettings"].toBool()) {
247 auth["User"] = msg["User"];
248 auth["Password"] = msg["Password"];
250 msg.remove("Password");
251 qDebug() << "Initializing storage provider" << msg["Type"].toString();
252 QString hname = msg["Type"].toString().toLower();
253 hname[0] = hname[0].toUpper();
254 hname = "initStorage" + hname;
255 if (!QMetaObject::invokeMethod(this, hname.toAscii(), Q_RETURN_ARG(bool, configured), Q_ARG(QVariantMap, msg), Q_ARG(bool, true))) {
256 qWarning("No database backend configured.");
259 // notify client to start wizard again
260 qWarning("Core is currently not configured!");
262 reply["StartWizard"] = true;
263 reply["StorageProviders"] = availableStorageProviders();
264 SignalProxy::writeDataToDevice(socket, reply);
266 // write coresettings
268 s.setDatabaseSettings(msg);
269 // write admin user to database & make the core listen again to connections
270 storage->addUser(auth["User"].toString(), auth["Password"].toString());
272 // continue the normal procedure
273 processClientInit(socket, auth);
276 // notify client to start wizard
278 reply["StartWizard"] = true;
279 reply["StorageProviders"] = availableStorageProviders();
280 SignalProxy::writeDataToDevice(socket, reply);
284 QVariant Core::initSession(UserId uid) {
285 // Find or create session for validated user
287 if(sessions.contains(uid))
288 sess = sessions[uid];
290 sess = createSession(uid);
292 reply["SessionState"] = sess->sessionState();
296 QStringList Core::availableStorageProviders() {
297 QStringList storageProviders;
298 if (SqliteStorage::isAvailable()) {
299 storageProviders.append(SqliteStorage::displayName());
302 storageProviders.append("MySQL");
304 return storageProviders;