Fixed linker error caused by not running MOC on ircuser.h.
[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   delete instanceptr;
37   instanceptr = 0;
38 }
39
40 Core::Core() {
41
42 }
43
44 void Core::init() {
45   if(!SqliteStorage::isAvailable()) {
46     qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
47   }
48   //SqliteStorage::init();
49   storage = new SqliteStorage();
50   connect(Global::instance(), SIGNAL(dataPutLocally(UserId, QString)), this, SLOT(updateGlobalData(UserId, QString)));
51   connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
52   //startListening(); // FIXME
53   guiUser = 0;
54   /*
55   if(Global::runMode == Global::Monolithic) {  // TODO Make GUI user configurable
56     try {
57       guiUser = storage->validateUser("Default", "password");
58     } catch(Storage::AuthError) {
59       guiUser = storage->addUser("Default", "password");
60     }
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::localSession() {
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((ClientSignal)sigdata[0].toInt() == GS_UPDATE_GLOBAL_DATA) {
142         processClientUpdate(socket, sigdata[1].toString(), sigdata[2]);
143       } else {
144         sessions[validClients[socket]]->processSignal((ClientSignal)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 QVariant Core::connectLocalClient(QString user, QString passwd) {
174   UserId uid = instance()->storage->validateUser(user, passwd);
175   QVariant reply = instance()->initSession(uid);
176   instance()->guiUser = uid;
177   Global::setGuiUser(uid);
178   qDebug() << "Local client connected.";
179   return reply;
180 }
181
182 QVariant Core::disconnectLocalClient() {
183   qDebug() << "Local client disconnected.";
184   instance()->guiUser = 0;
185   Global::setGuiUser(0);
186 }
187
188 void Core::processClientInit(QTcpSocket *socket, const QVariant &v) {
189   VarMap msg = v.toMap();
190   if(msg["GUIProtocol"].toUInt() != GUI_PROTOCOL) {
191     //qWarning() << "Client version mismatch.";
192     throw Exception("GUI client version mismatch");
193   }
194     // Auth
195   UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString());  // throws exception if this failed
196   VarMap reply = initSession(uid).toMap();
197   validClients[socket] = uid;
198   QList<QVariant> sigdata;
199   sigdata.append(CS_CORE_STATE); sigdata.append(QVariant(reply)); sigdata.append(QVariant()); sigdata.append(QVariant());
200   writeDataToDevice(socket, QVariant(sigdata));
201 }
202
203 QVariant Core::initSession(UserId uid) {
204   // Find or create session for validated user
205   CoreSession *sess;
206   if(sessions.contains(uid)) sess = sessions[uid];
207   else {
208     sess = createSession(uid);
209     //validClients[socket] = uid;
210   }
211   VarMap reply;
212   VarMap coreData;
213   QStringList dataKeys = Global::keys(uid);
214   foreach(QString key, dataKeys) {
215     coreData[key] = Global::data(uid, key);
216   }
217   reply["CoreData"] = coreData;
218   reply["SessionState"] = sess->sessionState();
219   return reply;
220 }
221
222 void Core::processClientUpdate(QTcpSocket *socket, QString key, const QVariant &data) {
223   UserId uid = validClients[socket];
224   Global::updateData(uid, key, data);
225   QList<QVariant> sigdata;
226   sigdata.append(CS_UPDATE_GLOBAL_DATA); sigdata.append(key); sigdata.append(data); sigdata.append(QVariant());
227   foreach(QTcpSocket *s, validClients.keys()) {
228     if(validClients[s] == uid && s != socket) writeDataToDevice(s, QVariant(sigdata));
229   }
230 }
231
232 void Core::updateGlobalData(UserId uid, QString key) {
233   QVariant data = Global::data(uid, key);
234   QList<QVariant> sigdata;
235   sigdata.append(CS_UPDATE_GLOBAL_DATA); sigdata.append(key); sigdata.append(data); sigdata.append(QVariant());
236   foreach(QTcpSocket *socket, validClients.keys()) {
237     if(validClients[socket] == uid) writeDataToDevice(socket, QVariant(sigdata));
238   }
239 }
240
241 void Core::recvProxySignal(CoreSignal sig, QVariant arg1, QVariant arg2, QVariant arg3) {
242   CoreSession *sess = qobject_cast<CoreSession*>(sender());
243   Q_ASSERT(sess);
244   UserId uid = sess->userId();
245   QList<QVariant> sigdata;
246   sigdata.append(sig); sigdata.append(arg1); sigdata.append(arg2); sigdata.append(arg3);
247   //qDebug() << "Sending signal: " << sigdata;
248   foreach(QTcpSocket *socket, validClients.keys()) {
249     if(validClients[socket] == uid) writeDataToDevice(socket, QVariant(sigdata));
250   }
251 }