b1815d9c517282decfe8128d66d59ccfb252650c
[quassel.git] / 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 "server.h"
23 #include "global.h"
24 #include "util.h"
25 #include "coreproxy.h"
26 #include "sqlitestorage.h"
27
28 #include <QtSql>
29 #include <QSettings>
30
31 Core *Core::instanceptr = 0;
32
33 Core * Core::instance() {
34   if(instanceptr) return instanceptr;
35   instanceptr = new Core();
36   instanceptr->init();
37   return instanceptr;
38 }
39
40 void Core::destroy() {
41   delete instanceptr;
42   instanceptr = 0;
43 }
44
45 Core::Core() {
46
47 }
48
49 void Core::init() {
50   if(!SqliteStorage::isAvailable()) {
51     qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
52   }
53   //SqliteStorage::init();
54   storage = new SqliteStorage();
55   connect(Global::instance(), SIGNAL(dataPutLocally(UserId, QString)), this, SLOT(updateGlobalData(UserId, QString)));
56   connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
57   //startListening(); // FIXME
58   if(Global::runMode == Global::Monolithic) {  // TODO Make GUI user configurable
59     guiUser = storage->validateUser("Default", "password");
60     if(!guiUser) guiUser = storage->addUser("Default", "password");
61     Q_ASSERT(guiUser);
62     Global::setGuiUser(guiUser);
63     createSession(guiUser);
64   } else guiUser = 0;
65
66   // Read global settings from config file
67   QSettings s;
68   s.beginGroup("Global");
69   foreach(QString unum, s.childGroups()) {
70     UserId uid = unum.toUInt();
71     s.beginGroup(unum);
72     foreach(QString key, s.childKeys()) {
73       Global::updateData(uid, key, s.value(key));
74     }
75     s.endGroup();
76   }
77   s.endGroup();
78 }
79
80 Core::~Core() {
81   foreach(QTcpSocket *sock, validClients.keys()) {
82     delete sock;
83   }
84   qDeleteAll(sessions);
85   delete storage;
86 }
87
88 CoreSession *Core::session(UserId uid) {
89   Core *core = instance();
90   if(core->sessions.contains(uid)) return core->sessions[uid];
91   else return 0;
92 }
93
94 CoreSession *Core::guiSession() {
95   Core *core = instance();
96   if(core->guiUser && core->sessions.contains(core->guiUser)) return core->sessions[core->guiUser];
97   else return 0;
98 }
99
100 CoreSession *Core::createSession(UserId uid) {
101   Core *core = instance();
102   Q_ASSERT(!core->sessions.contains(uid));
103   CoreSession *sess = new CoreSession(uid, core->storage);
104   core->sessions[uid] = sess;
105   connect(sess, SIGNAL(proxySignal(CoreSignal, QVariant, QVariant, QVariant)), core, SLOT(recvProxySignal(CoreSignal, QVariant, QVariant, QVariant)));
106   return sess;
107 }
108
109
110 bool Core::startListening(uint port) {
111   if(!server.listen(QHostAddress::Any, port)) {
112     qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii());
113     return false;
114   }
115   qDebug() << "Listening for GUI clients on port" << server.serverPort();
116   return true;
117 }
118
119 void Core::stopListening() {
120   server.close();
121   qDebug() << "No longer listening for GUI clients.";
122 }
123
124 void Core::incomingConnection() {
125   // TODO implement SSL
126   QTcpSocket *socket = server.nextPendingConnection();
127   connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
128   connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
129   blockSizes.insert(socket, (quint32)0);
130   qDebug() << "Client connected from " << socket->peerAddress().toString();
131 }
132
133 void Core::clientHasData() {
134   QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
135   Q_ASSERT(socket && blockSizes.contains(socket));
136   quint32 bsize = blockSizes.value(socket);
137   QVariant item;
138   while(readDataFromDevice(socket, bsize, item)) {
139     if(validClients.contains(socket)) {
140       QList<QVariant> sigdata = item.toList();
141       if((GUISignal)sigdata[0].toInt() == GS_UPDATE_GLOBAL_DATA) {
142         processClientUpdate(socket, sigdata[1].toString(), sigdata[2]);
143       } else {
144         sessions[validClients[socket]]->processSignal((GUISignal)sigdata[0].toInt(), sigdata[1], sigdata[2], sigdata[3]);
145       }
146     } else {
147       // we need to auth the client
148       try {
149         processClientInit(socket, item);
150       } catch(Storage::AuthError) {
151         qWarning() << "Authentification error!";  // FIXME
152         socket->close();
153         return;
154       } catch(Exception e) {
155         qWarning() << "Client init error:" << e.msg();
156         socket->close();
157         return;
158       }
159     }
160     blockSizes[socket] = bsize = 0;
161   }
162   blockSizes[socket] = bsize;
163 }
164
165 void Core::clientDisconnected() {
166   QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
167   blockSizes.remove(socket);
168   validClients.remove(socket);
169   qDebug() << "Client disconnected.";
170   // TODO remove unneeded sessions - if necessary/possible...
171 }
172
173 void Core::processClientInit(QTcpSocket *socket, const QVariant &v) {
174   VarMap msg = v.toMap();
175   if(msg["GUIProtocol"].toUInt() != GUI_PROTOCOL) {
176     //qWarning() << "Client version mismatch.";
177     throw Exception("GUI client version mismatch");
178   }
179   // Auth
180   UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString());  // throws exception if this failed
181
182   // Find or create session for validated user
183   CoreSession *sess;
184   if(sessions.contains(uid)) sess = sessions[uid];
185   else {
186     sess = createSession(uid);
187     validClients[socket] = uid;
188   }
189   VarMap reply;
190   VarMap coreData;
191   // FIXME
192   QStringList dataKeys = Global::keys(uid);
193   QString key;
194   foreach(key, dataKeys) {
195     coreData[key] = Global::data(key);
196   }
197   reply["CoreData"] = coreData;
198   reply["SessionState"] = sess->sessionState();
199   QList<QVariant> sigdata;
200   sigdata.append(CS_CORE_STATE); sigdata.append(QVariant(reply)); sigdata.append(QVariant()); sigdata.append(QVariant());
201   writeDataToDevice(socket, QVariant(sigdata));
202   sess->sendServerStates();
203 }
204
205 void Core::processClientUpdate(QTcpSocket *socket, QString key, const QVariant &data) {
206   UserId uid = validClients[socket];
207   Global::updateData(uid, key, data);
208   QList<QVariant> sigdata;
209   sigdata.append(CS_UPDATE_GLOBAL_DATA); sigdata.append(key); sigdata.append(data); sigdata.append(QVariant());
210   foreach(QTcpSocket *s, validClients.keys()) {
211     if(validClients[s] == uid && s != socket) writeDataToDevice(s, QVariant(sigdata));
212   }
213 }
214
215 void Core::updateGlobalData(UserId uid, QString key) {
216   QVariant data = Global::data(uid, key);
217   QList<QVariant> sigdata;
218   sigdata.append(CS_UPDATE_GLOBAL_DATA); sigdata.append(key); sigdata.append(data); sigdata.append(QVariant());
219   foreach(QTcpSocket *socket, validClients.keys()) {
220     if(validClients[socket] == uid) writeDataToDevice(socket, QVariant(sigdata));
221   }
222 }
223
224 void Core::recvProxySignal(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
225   CoreSession *sess = qobject_cast<CoreSession*>(sender());
226   Q_ASSERT(sess);
227   UserId uid = sess->userId();
228   QList<QVariant> sigdata;
229   sigdata.append(sig); sigdata.append(arg1); sigdata.append(arg2); sigdata.append(arg3);
230   //qDebug() << "Sending signal: " << sigdata;
231   foreach(QTcpSocket *socket, validClients.keys()) {
232     if(validClients[socket] == uid) writeDataToDevice(socket, QVariant(sigdata));
233   }
234 }
235
236 /*
237   // Read global settings from config file
238   QSettings s;
239   s.beginGroup("Global");
240   QString key;
241   foreach(key, s.childKeys()) {
242     global->updateData(key, s.value(key));
243   }
244
245   global->updateData("CoreReady", true);
246   // Now that we are in sync, we can connect signals to automatically store further updates.
247   // I don't think we care if global data changed locally or if it was updated by a client. 
248   connect(global, SIGNAL(dataUpdatedRemotely(QString)), SLOT(globalDataUpdated(QString)));
249   connect(global, SIGNAL(dataPutLocally(QString)), SLOT(globalDataUpdated(QString)));
250
251 }
252   */
253
254 CoreSession::CoreSession(UserId uid, Storage *_storage) : user(uid), storage(_storage) {
255   coreProxy = new CoreProxy();
256   connect(coreProxy, SIGNAL(send(CoreSignal, QVariant, QVariant, QVariant)), this, SIGNAL(proxySignal(CoreSignal, QVariant, QVariant, QVariant)));
257
258   connect(coreProxy, SIGNAL(requestServerStates()), this, SIGNAL(serverStateRequested()));
259   connect(coreProxy, SIGNAL(gsRequestConnect(QStringList)), this, SLOT(connectToIrc(QStringList)));
260   connect(coreProxy, SIGNAL(gsUserInput(BufferId, QString)), this, SLOT(msgFromGui(BufferId, QString)));
261   connect(coreProxy, SIGNAL(gsImportBacklog()), storage, SLOT(importOldBacklog()));
262   connect(coreProxy, SIGNAL(gsRequestBacklog(BufferId, QVariant, QVariant)), this, SLOT(sendBacklog(BufferId, QVariant, QVariant)));
263   connect(this, SIGNAL(displayMsg(Message)), coreProxy, SLOT(csDisplayMsg(Message)));
264   connect(this, SIGNAL(displayStatusMsg(QString, QString)), coreProxy, SLOT(csDisplayStatusMsg(QString, QString)));
265   connect(this, SIGNAL(backlogData(BufferId, QList<QVariant>, bool)), coreProxy, SLOT(csBacklogData(BufferId, QList<QVariant>, bool)));
266   connect(this, SIGNAL(bufferIdUpdated(BufferId)), coreProxy, SLOT(csUpdateBufferId(BufferId)));
267   connect(storage, SIGNAL(bufferIdUpdated(BufferId)), coreProxy, SLOT(csUpdateBufferId(BufferId)));
268   connect(Global::instance(), SIGNAL(dataUpdatedRemotely(UserId, QString)), this, SLOT(globalDataUpdated(UserId, QString)));
269   connect(Global::instance(), SIGNAL(dataPutLocally(UserId, QString)), this, SLOT(globalDataUpdated(UserId, QString)));
270
271 }
272
273 CoreSession::~CoreSession() {
274
275 }
276
277 UserId CoreSession::userId() {
278   return user;
279 }
280
281 void CoreSession::processSignal(GUISignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
282   coreProxy->recv(sig, arg1, arg2, arg3);
283 }
284
285 void CoreSession::globalDataUpdated(UserId uid, QString key) {
286   Q_ASSERT(uid == userId());
287   QVariant data = Global::data(userId(), key);
288   QSettings s;
289   s.setValue(QString("Global/%1/").arg(userId())+key, data);
290 }
291
292 void CoreSession::connectToIrc(QStringList networks) {
293   foreach(QString net, networks) {
294     if(servers.contains(net)) {
295
296     } else {
297       Server *server = new Server(userId(), net);
298       connect(this, SIGNAL(serverStateRequested()), server, SLOT(sendState()));
299       connect(this, SIGNAL(connectToIrc(QString)), server, SLOT(connectToIrc(QString)));
300       connect(this, SIGNAL(disconnectFromIrc(QString)), server, SLOT(disconnectFromIrc(QString)));
301       connect(this, SIGNAL(msgFromGui(QString, QString, QString)), server, SLOT(userInput(QString, QString, QString)));
302       connect(server, SIGNAL(serverState(QString, VarMap)), coreProxy, SLOT(csServerState(QString, VarMap)));
303       //connect(server, SIGNAL(displayMsg(Message)), this, SLOT(recvMessageFromServer(Message)));
304       connect(server, SIGNAL(displayMsg(Message::Type, QString, QString, QString, quint8)), this, SLOT(recvMessageFromServer(Message::Type, QString, QString, QString, quint8)));
305       connect(server, SIGNAL(displayStatusMsg(QString)), this, SLOT(recvStatusMsgFromServer(QString)));
306       connect(server, SIGNAL(modeSet(QString, QString, QString)), coreProxy, SLOT(csModeSet(QString, QString, QString)));
307       connect(server, SIGNAL(topicSet(QString, QString, QString)), coreProxy, SLOT(csTopicSet(QString, QString, QString)));
308       connect(server, SIGNAL(nickAdded(QString, QString, VarMap)), coreProxy, SLOT(csNickAdded(QString, QString, VarMap)));
309       connect(server, SIGNAL(nickRenamed(QString, QString, QString)), coreProxy, SLOT(csNickRenamed(QString, QString, QString)));
310       connect(server, SIGNAL(nickRemoved(QString, QString)), coreProxy, SLOT(csNickRemoved(QString, QString)));
311       connect(server, SIGNAL(nickUpdated(QString, QString, VarMap)), coreProxy, SLOT(csNickUpdated(QString, QString, VarMap)));
312       connect(server, SIGNAL(ownNickSet(QString, QString)), coreProxy, SLOT(csOwnNickSet(QString, QString)));
313       connect(server, SIGNAL(queryRequested(QString, QString)), coreProxy, SLOT(csQueryRequested(QString, QString)));
314       // TODO add error handling
315       connect(server, SIGNAL(connected(QString)), coreProxy, SLOT(csServerConnected(QString)));
316       connect(server, SIGNAL(disconnected(QString)), this, SLOT(serverDisconnected(QString)));
317
318       server->start();
319       servers[net] = server;
320     }
321     emit connectToIrc(net);
322   }
323 }
324
325 void CoreSession::serverDisconnected(QString net) {
326   delete servers[net];
327   servers.remove(net);
328   coreProxy->csServerDisconnected(net);
329 }
330
331 void CoreSession::msgFromGui(BufferId bufid, QString msg) {
332   emit msgFromGui(bufid.network(), bufid.buffer(), msg);
333 }
334
335 // ALL messages coming pass through these functions before going to the GUI.
336 // So this is the perfect place for storing the backlog and log stuff.
337
338 void CoreSession::recvMessageFromServer(Message::Type type, QString target, QString text, QString sender, quint8 flags) {
339   Server *s = qobject_cast<Server*>(this->sender());
340   Q_ASSERT(s);
341   BufferId buf;
342   if((flags & Message::PrivMsg) && !(flags & Message::Self)) {
343     buf = storage->getBufferId(user, s->getNetwork(), nickFromMask(sender));
344   } else {
345     buf = storage->getBufferId(user, s->getNetwork(), target);
346   }
347   Message msg(buf, type, text, sender, flags);
348   msg.msgId = storage->logMessage(msg); //qDebug() << msg.msgId;
349   Q_ASSERT(msg.msgId);
350   emit displayMsg(msg);
351 }
352
353 void CoreSession::recvStatusMsgFromServer(QString msg) {
354   Server *s = qobject_cast<Server*>(sender());
355   Q_ASSERT(s);
356   emit displayStatusMsg(s->getNetwork(), msg);
357 }
358
359
360 QList<BufferId> CoreSession::buffers() const {
361   return storage->requestBuffers(user);
362 }
363
364
365 QVariant CoreSession::sessionState() {
366   VarMap v;
367   QList<QVariant> bufs;
368   foreach(BufferId id, storage->requestBuffers(user)) { bufs.append(QVariant::fromValue(id)); }
369   v["Buffers"] = bufs;
370
371   return v;
372 }
373
374 void CoreSession::sendServerStates() {
375   emit serverStateRequested();
376 }
377
378 void CoreSession::sendBacklog(BufferId id, QVariant v1, QVariant v2) {
379   QList<QVariant> log;
380   QList<Message> msglist;
381   if(v1.type() == QVariant::DateTime) {
382
383
384   } else {
385     msglist = storage->requestMsgs(id, v1.toInt(), v2.toInt());
386   }
387
388   // Send messages out in smaller packages - we don't want to make the signal data too large!
389   for(int i = 0; i < msglist.count(); i++) {
390     log.append(QVariant::fromValue(msglist[i]));
391     if(log.count() >= 5) {
392       emit backlogData(id, log, i >= msglist.count() - 1);
393       log.clear();
394     }
395   }
396   if(log.count() > 0) emit backlogData(id, log, true);
397 }
398
399
400 //Core *core = 0;