A warning message is now displayed when your nickname is already taken or not valid...
[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 void Core::init() {
44   if(!SqliteStorage::isAvailable()) {
45     qFatal("Sqlite is currently required! Please make sure your Qt library has sqlite support enabled.");
46   }
47   //SqliteStorage::init();
48   storage = new SqliteStorage();
49   connect(&server, SIGNAL(newConnection()), this, SLOT(incomingConnection()));
50   startListening(); // FIXME make configurable
51   guiUser = 0;
52 }
53
54 Core::~Core() {
55   qDeleteAll(sessions);
56   delete storage;
57 }
58
59 CoreSession *Core::session(UserId uid) {
60   Core *core = instance();
61   if(core->sessions.contains(uid)) return core->sessions[uid];
62   else return 0;
63 }
64
65 CoreSession *Core::localSession() {
66   Core *core = instance();
67   if(core->guiUser && core->sessions.contains(core->guiUser)) return core->sessions[core->guiUser];
68   else return 0;
69 }
70
71 CoreSession *Core::createSession(UserId uid) {
72   Core *core = instance();
73   Q_ASSERT(!core->sessions.contains(uid));
74   CoreSession *sess = new CoreSession(uid, core->storage);
75   core->sessions[uid] = sess;
76   return sess;
77 }
78
79
80 bool Core::startListening(uint port) {
81   if(!server.listen(QHostAddress::Any, port)) {
82     qWarning(QString(QString("Could not open GUI client port %1: %2").arg(port).arg(server.errorString())).toAscii());
83     return false;
84   }
85   qDebug() << "Listening for GUI clients on port" << server.serverPort();
86   return true;
87 }
88
89 void Core::stopListening() {
90   server.close();
91   qDebug() << "No longer listening for GUI clients.";
92 }
93
94 void Core::incomingConnection() {
95   // TODO implement SSL
96   // TODO While
97   QTcpSocket *socket = server.nextPendingConnection();
98   connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
99   connect(socket, SIGNAL(readyRead()), this, SLOT(clientHasData()));
100   blockSizes.insert(socket, (quint32)0);
101   qDebug() << "Client connected from " << socket->peerAddress().toString();
102 }
103
104 void Core::clientHasData() {
105   QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
106   Q_ASSERT(socket && blockSizes.contains(socket));
107   quint32 bsize = blockSizes.value(socket);
108   QVariant item;
109   if(readDataFromDevice(socket, bsize, item)) {
110     // we need to auth the client
111     try {
112       processClientInit(socket, item);
113     } catch(Storage::AuthError) {
114       qWarning() << "Authentification error!";  // FIXME: send auth error to client
115       socket->close();
116       return;
117     } catch(Exception e) {
118       qWarning() << "Client init error:" << e.msg();
119       socket->close();
120       return;
121     }
122   }
123   blockSizes[socket] = bsize = 0;  // FIXME blockSizes aufräum0rn!
124 }
125
126 // FIXME: no longer called, since connection handling is now in SignalProxy
127 void Core::clientDisconnected() {
128   QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
129   blockSizes.remove(socket);
130   qDebug() << "Client disconnected.";
131   // TODO remove unneeded sessions - if necessary/possible...
132 }
133
134 QVariant Core::connectLocalClient(QString user, QString passwd) {
135   UserId uid = instance()->storage->validateUser(user, passwd);
136   QVariant reply = instance()->initSession(uid);
137   instance()->guiUser = uid;
138   qDebug() << "Local client connected.";
139   return reply;
140 }
141
142 void Core::disconnectLocalClient() {
143   qDebug() << "Local client disconnected.";
144   instance()->guiUser = 0;
145 }
146
147 void Core::processClientInit(QTcpSocket *socket, const QVariant &v) {
148   QVariantMap msg = v.toMap();
149   if(msg["GuiProtocol"].toUInt() != GUI_PROTOCOL) {
150     //qWarning() << "Client version mismatch.";
151     throw Exception("GUI client version mismatch");
152   }
153     // Auth
154   UserId uid = storage->validateUser(msg["User"].toString(), msg["Password"].toString());  // throws exception if this failed
155   QVariant reply = initSession(uid);
156   disconnect(socket, 0, this, 0);
157   sessions[uid]->addClient(socket);
158   qDebug() << "Client initialized successfully.";
159   writeDataToDevice(socket, reply);
160 }
161
162 QVariant Core::initSession(UserId uid) {
163   // Find or create session for validated user
164   CoreSession *sess;
165   if(sessions.contains(uid))
166     sess = sessions[uid];
167   else
168     sess = createSession(uid);
169   QVariantMap reply;
170   reply["SessionState"] = sess->sessionState();
171   return reply;
172 }