YES! We finally have dynamic signals between Core and Client, meaning that arbitrary
[quassel.git] / src / core / core.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by The Quassel IRC Development Team             *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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) any later version.                                   *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "core.h"
22 #include "coresession.h"
23 #include "sqlitestorage.h"
24 #include "util.h"
25
26 Core *Core::instanceptr = 0;
27
28 Core * Core::instance() {
29   if(instanceptr) return instanceptr;
30   instanceptr = new Core();
31   instanceptr->init();
32   return instanceptr;
33 }
34
35 void Core::destroy() {
36   //instanceptr->deleteLater();
37   delete instanceptr;
38   instanceptr = 0;
39 }
40
41 Core::Core() {
42
43 }
44
45 void Core::init() {
46   if(!SqliteStorage::isAvailable()) {
47     qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
48   }
49   //SqliteStorage::init();
50   storage = new SqliteStorage();
51   connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
52   startListening(); // FIXME make configurable
53   guiUser = 0;
54 }
55
56 Core::~Core() {
57   //foreach(QTcpSocket *sock, validClients.keys()) {
58   //  delete sock;
59   //}
60   qDeleteAll(sessions);
61   delete storage;
62 }
63
64 CoreSession *Core::session(UserId uid) {
65   Core *core = instance();
66   if(core->sessions.contains(uid)) return core->sessions[uid];
67   else return 0;
68 }
69
70 CoreSession *Core::localSession() {
71   Core *core = instance();
72   if(core->guiUser && core->sessions.contains(core->guiUser)) return core->sessions[core->guiUser];
73   else return 0;
74 }
75
76 CoreSession *Core::createSession(UserId uid) {
77   Core *core = instance();
78   Q_ASSERT(!core->sessions.contains(uid));
79   CoreSession *sess = new CoreSession(uid, core->storage);
80   core->sessions[uid] = sess;
81   //connect(sess, SIGNAL(proxySignal(CoreSignal, QVariant, QVariant, QVariant)), core, SLOT(recvProxySignal(CoreSignal, QVariant, QVariant, QVariant)));
82   return sess;
83 }
84
85
86 bool Core::startListening(uint port) {
87   if(!server.listen(QHostAddress::Any, port)) {
88     qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii());
89     return false;
90   }
91   qDebug() << "Listening for GUI clients on port" << server.serverPort();
92   return true;
93 }
94
95 void Core::stopListening() {
96   server.close();
97   qDebug() << "No longer listening for GUI clients.";
98 }
99
100 void Core::incomingConnection() {
101   // TODO implement SSL
102   QTcpSocket *socket = server.nextPendingConnection();
103   connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
104   connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
105   blockSizes.insert(socket, (quint32)0);
106   qDebug() << "Client connected from " << socket->peerAddress().toString();
107 }
108
109 void Core::clientHasData() {
110   QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
111   Q_ASSERT(socket && blockSizes.contains(socket));
112   quint32 bsize = blockSizes.value(socket);
113   QVariant item;
114   if(readDataFromDevice(socket, bsize, item)) {
115     /* this is probably obsolete now */
116     if(validClients.contains(socket)) {
117       Q_ASSERT(false);
118       //QList<QVariant> sigdata = item.toList();
119       //sessions[validClients[socket]]->processSignal((ClientSignal)sigdata[0].toInt(), sigdata[1], sigdata[2], sigdata[3]);
120     } else {
121       // we need to auth the client
122       try {
123         processClientInit(socket, item);
124       } catch(Storage::AuthError) {
125         qWarning() << "Authentification error!";  // FIXME
126         socket->close();
127         return;
128       } catch(Exception e) {
129         qWarning() << "Client init error:" << e.msg();
130         socket->close();
131         return;
132       }
133     }
134     blockSizes[socket] = bsize = 0;  // FIXME blockSizes aufräum0rn!
135   }
136   blockSizes[socket] = bsize;
137 }
138
139 void Core::clientDisconnected() {
140   QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
141   blockSizes.remove(socket);
142   validClients.remove(socket);
143   qDebug() << "Client disconnected.";
144   // TODO remove unneeded sessions - if necessary/possible...
145 }
146
147 QVariant Core::connectLocalClient(QString user, QString passwd) {
148   UserId uid = instance()->storage->validateUser(user, passwd);
149   QVariant reply = instance()->initSession(uid);
150   instance()->guiUser = uid;
151   qDebug() << "Local client connected.";
152   return reply;
153 }
154
155 void Core::disconnectLocalClient() {
156   qDebug() << "Local client disconnected.";
157   instance()->guiUser = 0;
158 }
159
160 void Core::processClientInit(QTcpSocket *socket, const QVariant &v) {
161   QVariantMap msg = v.toMap();
162   if(msg["GuiProtocol"].toUInt() != GUI_PROTOCOL) {
163     //qWarning() << "Client version mismatch.";
164     throw Exception("GUI client version mismatch");
165   }
166     // Auth
167   UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString());  // throws exception if this failed
168   QVariant reply = initSession(uid);
169   validClients[socket] = uid;  // still needed? FIXME
170   //QList<QVariant> sigdata;
171   //sigdata.append(CS_CORE_STATE); sigdata.append(reply); sigdata.append(QVariant()); sigdata.append(QVariant());
172   disconnect(socket, 0, this, 0);
173   sessions[uid]->addClient(socket);
174   writeDataToDevice(socket, reply);
175 }
176
177 QVariant Core::initSession(UserId uid) {
178   // Find or create session for validated user
179   CoreSession *sess;
180   if(sessions.contains(uid)) sess = sessions[uid];
181   else {
182     sess = createSession(uid);
183     //validClients[socket] = uid;
184   }
185   QVariantMap reply;
186   reply["SessionState"] = sess->sessionState();
187   return reply;
188 }
189
190 /*
191 void Core::recvProxySignal(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
192   CoreSession *sess = qobject_cast<CoreSession*>(sender());
193   Q_ASSERT(sess);
194   UserId uid = sess->userId();
195   QList<QVariant> sigdata;
196   sigdata.append(sig); sigdata.append(arg1); sigdata.append(arg2); sigdata.append(arg3);
197   //qDebug() << "Sending signal: " << sigdata;
198   foreach(QTcpSocket *socket, validClients.keys()) {
199     if(validClients[socket] == uid) writeDataToDevice(socket, QVariant(sigdata));
200   }
201 }
202 */